pgqd/autogen.sh0000775000401600040160000000005113175113043012046 0ustar cbecbe#! /bin/sh ./lib/mk/std-autogen.sh lib pgqd/config.mak.in0000664000401600040160000000126313175113043012417 0ustar cbecbe PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_STRING = @PACKAGE_STRING@ SUFFIX = @SUFFIX@ prefix = @prefix@ datarootdir = @datarootdir@ exec_prefix = @exec_prefix@ datadir = @datadir@ docdir = @docdir@$(SUFFIX) mandir = @mandir@ bindir = @bindir@ PG_CONFIG = @PG_CONFIG@ DESTDIR = SED = @SED@ GREP = @GREP@ EGREP = @EGREP@ MKDIR_P = @MKDIR_P@ LN_S = @LN_S@ CC = @CC@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ @WFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ SHELL = @SHELL@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_DATA = @INSTALL_DATA@ BININSTALL = $(INSTALL_SCRIPT) pgqd/configure.ac0000664000401600040160000000165513175113043012346 0ustar cbecbednl Process this file with autoconf to produce a configure script. AC_INIT(pgqd, 3.3) AC_CONFIG_SRCDIR(src/pgqd.c) AC_CONFIG_HEADER(lib/usual/config.h) AC_PREREQ([2.59]) dnl Find PostgreSQL pg_config AC_ARG_WITH(pgconfig, [ --with-pgconfig=PG_CONFIG path to pg_config (default: pg_config)], [ AC_MSG_CHECKING(for pg_config) PG_CONFIG=$withval AC_MSG_RESULT($PG_CONFIG)], [ AC_PATH_PROGS(PG_CONFIG, pg_config) ]) test -n "$PG_CONFIG" || AC_MSG_ERROR([Cannot continue without pg_config]) AC_USUAL_PORT_CHECK AC_USUAL_PROGRAM_CHECK AC_USUAL_HEADER_CHECK AC_USUAL_TYPE_CHECK AC_USUAL_FUNCTION_CHECK dnl Postres headers on Solaris define incompat unsetenv without that AC_CHECK_FUNCS(unsetenv) dnl Optional use of libevent AC_SEARCH_LIBS(clock_gettime, rt) AC_USUAL_LIBEVENT dnl Needed on SmartOS (Solaris) AC_SEARCH_LIBS([socket],[socket]) AC_USUAL_DEBUG AC_USUAL_CASSERT dnl Write result AC_CONFIG_FILES([config.mak]) AC_OUTPUT pgqd/debian/0000775000401600040160000000000013175113043011273 5ustar cbecbepgqd/debian/compat0000664000401600040160000000000213175113043012471 0ustar cbecbe9 pgqd/debian/source/0000775000401600040160000000000013175113043012573 5ustar cbecbepgqd/debian/source/format0000664000401600040160000000001413175113043014001 0ustar cbecbe3.0 (quilt) pgqd/debian/pgqd.ini0000664000401600040160000000111013175113043012720 0ustar cbecbe[pgqd] # where to log #logfile = /var/log/pgqd.log # pidfile pidfile = /var/run/pgqd.pid ## optional parameters ## # libpq connect string without dbname= #base_connstr = # startup db to query other databases #initial_database = template1 # limit ticker to specific databases #database_list = # log into syslog syslog = 1 syslog_ident = pgqd ## optional timeouts ## # how often to check for new databases #check_period = 60 # how often to flush retry queue #retry_period = 30 # how often to do maintentance #maint_period = 120 # how often to run ticker #ticker_period = 1 pgqd/debian/rules0000775000401600040160000000036113175113043012353 0ustar cbecbe#! /usr/bin/make -f %: dh $@ override_dh_auto_configure: ./autogen.sh dh_auto_configure override_dh_auto_install: dh_auto_install -- docdir=/usr/share/doc/pgqd override_dh_auto_clean: if test -f config.mak; then $(MAKE) clean; fi pgqd/debian/init0000664000401600040160000000711613175113043012166 0ustar cbecbe#! /bin/sh ### BEGIN INIT INFO # Provides: pgbouncer # Required-Start: $local_fs $remote_fs $network $named $syslog # Required-Stop: $local_fs $remote_fs $network $named $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: PgBouncer initscript # Description: PgBouncer is connection pooler for PostgreSQL ### END INIT INFO PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="PgQ daemon" NAME=pgqd USER=pgqd DAEMON=/usr/bin/$NAME DAEMON_ARGS="-d -q -u $USER /etc/pgqd.ini" PIDFILE=/var/run/pgbouncer/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables #. /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions SSD="start-stop-daemon --quiet --pidfile $PIDFILE --user $USER --name $NAME --exec $DAEMON" # # Function that starts the daemon/service # do_start() { test -d /var/run/pgqd || install -d -m 755 -o $USER -g postgres /var/run/pgqd # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started $SSD --start --test > /dev/null || return 1 $SSD --start -- $DAEMON_ARGS || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred $SSD --stop --retry=TERM/30/KILL/5 RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. $SSD --stop --oknodo --retry=0/30/KILL/5 [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # $SSD --stop --signal 1 return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; reload|force-reload) log_daemon_msg "Reloading $DESC" "$NAME" do_reload log_end_msg $? ;; restart) log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload}" >&2 exit 3 ;; esac : pgqd/debian/postinst0000664000401600040160000000203213175113043013076 0ustar cbecbe#!/bin/sh # postinst script for pgbouncer # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `configure' # * `abort-upgrade' # * `abort-remove' `in-favour' # # * `abort-remove' # * `abort-deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package PGQD_USER=pgqd case "$1" in configure) if getent passwd $PGQD_USER > /dev/null; then echo User $PGQD_USER already exists else adduser --system --disabled-login --no-create-home --home /etc --ingroup postgres $PGQD_USER fi chown root:root /etc/pgqd.ini ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 pgqd/debian/control0000664000401600040160000000116513175113043012701 0ustar cbecbeSource: pgqd Section: database Priority: optional Maintainer: Debian PostgreSQL Maintainers Uploaders: Christoph Berg Standards-Version: 4.1.1 Build-Depends: debhelper (>= 9), python-docutils, libevent-dev (>= 2.0), libpq-dev Package: pgqd Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, lsb-base Description: Queue maintenance daemon for PgQ PgQ provides generic queues for PostgreSQL. It allows multiple queues in one database, each queue can be consumed by multiple consumers. . This package contains the queue maintenance daemon. pgqd/debian/copyright0000664000401600040160000001743413175113043013237 0ustar cbecbeFormat: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Source: https://github.com/pgq/pgqd Files: * Copyright: Copyright (C) 2007-2016, Skytools Authors Copyright (C) 2007-2011 Marko Kreen, Skype Technologies OÜ License: ISC Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. . THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Files: debian/* Copyright: Copyright (C) 2011 Dimitri Fontaine License: ISC File: lib/test/attregex/testregex.c Copyright: Glenn Fowler , AT&T Research License: AT&T PLEASE: publish your tests so everyone can benefit . The following license covers testregex.c and all associated test data. . Permission is hereby granted, free of charge, to any person obtaining a copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following disclaimer: . THIS SOFTWARE IS PROVIDED BY AT&T ``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 AT&T 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. Files: lib/test/tinytest.[ch] Copyright: Copyright (C) 2009 Nick Mathewson License: Mathewson 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. File: lib/usual/getopt.c Copyright: Copyright (c) 2002 Todd C. Miller Copyright (c) 2000 The NetBSD Foundation, Inc. License: 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. . Sponsored in part by the Defense Advanced Research Projects Agency (DARPA) and Air Force Research Laboratory, Air Force . All rights reserved. . This code is derived from software contributed to The NetBSD Foundation by Dieter Baron and Thomas Klausner. . 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. File: lib/usual/crypto/hmac.c Copyright: Copyright (c) 2012 Daniel Farina License: Farina Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. . THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. File: lib/usual/tls/* Copyright: Copyright (c) 2014 Jeremie Courreges-Anglas Copyright (c) 2015 Joel Sing Copyright (c) 2015 Bob Beck Copyright (c) 2015 Reyk Floeter License: TLS 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. pgqd/debian/install0000664000401600040160000000002513175113043012661 0ustar cbecbedebian/pgqd.ini etc/ pgqd/debian/changelog0000664000401600040160000000016413175113043013146 0ustar cbecbepgqd (3.3-1) unstable; urgency=low * v3.3 -- Marko Kreen Fri, 03 Jun 2016 15:14:28 +0300 pgqd/lib/0000775000401600040160000000000013175113172010622 5ustar cbecbepgqd/lib/autogen.sh0000775000401600040160000000004313175113172012620 0ustar cbecbe#! /bin/sh ./mk/std-autogen.sh . pgqd/lib/config.mak.in0000664000401600040160000000244313175113172013171 0ustar cbecbePACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PORTNAME = @PORTNAME@ EXEEXT = @EXEEXT@ HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ CC = @CC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ WFLAGS = @WFLAGS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ AR = @AR@ ARFLAGS = @ARFLAGS@ RANLIB = @RANLIB@ LIBTOOL = @LIBTOOL@ SHELL = @SHELL@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_DATA = @INSTALL_DATA@ MKDIR_P = @MKDIR_P@ SED = @SED@ AWK = @AWK@ GREP = @GREP@ EGREP = @EGREP@ STRIP = @STRIP@ TLS_CPPFLAGS = @TLS_CPPFLAGS@ TLS_LDFLAGS = @TLS_LDFLAGS@ TLS_LIBS = @TLS_LIBS@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datarootdir = @datarootdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ docdir = @docdir@ mandir = @mandir@ libdir = @libdir@ localedir = @localedir@ pkgdatadir = @pkgdatadir@ pkgconfigdir = @pkgconfigdir@ abs_top_srcdir ?= @abs_top_srcdir@ abs_top_builddir ?= @abs_top_builddir@ nosub_top_srcdir ?= @top_srcdir@ nosub_top_builddir ?= @top_builddir@ enable_debug = @enable_debug@ pgqd/lib/find_modules.sh0000775000401600040160000000167113175113172013636 0ustar cbecbe#! /bin/sh set -e top="$1" # sanity check test -n "$top" || { echo "usage: $0 USUAL_DIR SRC ..." >&2 exit 1 } test -f "$top/usual/base.h" || { echo "usage: $0 USUAL_DIR SRC ..." >&2 exit 1 } shift test -n "$1" || exit 0 test -n "$AWK" || AWK=awk # return uniq module names, exclude already found ones grep_usual() { excl='excl["config"]=1' for m in $m_done; do excl="$excl;excl[\"$m\"]=1" done prog=' BEGIN { '"$excl"' } /^#include[ \t]*[<"]usual\// { p1 = index($0, "/"); p2 = index($0, "."); m = substr($0, p1+1, p2-p1-1); if (!excl[m]) print m; }' $AWK "$prog" "$@" | sort -u } # return module filename globs make_pats() { for m in "$@"; do echo "$top/usual/$m*.[ch]" done } # loop over grep until all mods are found m_done="" m_tocheck=`grep_usual "$@"` while test -n "$m_tocheck"; do m_done="$m_done $m_tocheck" pats=`make_pats $m_tocheck` m_tocheck=`grep_usual $pats` done # done echo $m_done pgqd/lib/mk/0000775000401600040160000000000013175113172011231 5ustar cbecbepgqd/lib/mk/temos/0000775000401600040160000000000013175113172012360 5ustar cbecbepgqd/lib/mk/temos/expected/0000775000401600040160000000000013175113172014161 5ustar cbecbepgqd/lib/mk/temos/expected/antimake1.txt0000664000401600040160000000453713175113172016605 0ustar cbecbe = Test Antimake = == Simplest usage == Here we avoid use of any autotools. First, this is the source file: .File: hello.c [source,c] ----------------------------------- #include int main(void) { printf("Hello, world\n"); return 0; } ----------------------------------- Here is corresponding Makefile: .File: Makefile [source,makefile] ----------------------------------- # This is target list - it's name describes target type # and how it is installed, it's value target files to be built. # bin - the targets will be installed under $(bindir) # PROGRAMS - the target is executable built from many sources bin_PROGRAMS = hello # The target 'hello'-s source file list. hello_SOURCES = hello.c # Run Antimake include antimake.mk ----------------------------------- Also install Antimake and we are ready to build: --------------------------------- $ cp ../../antimake.mk . $ ls Makefile antimake.mk hello.c --------------------------------- Build the project --------------------------------- $ make CC hello.c CCLD hello $ ls Makefile antimake.mk hello hello.c $ ./hello Hello, world --------------------------------- We can even install it already: --------------------------------- $ make install prefix=/opt DESTDIR=./inst INSTALL hello ./inst/opt/bin $ ls ./inst/opt/bin hello --------------------------------- For creating source package, we need to provide additional info: .File: Makefile [source,makefile] ----------------------------------- # Package name and version for tarball filename PACKAGE_NAME = myhello PACKAGE_VERSION = 1.0 # Non-source files to put into tarball EXTRA_DIST = Makefile antimake.mk bin_PROGRAMS = hello hello_SOURCES = hello.c include antimake.mk ----------------------------------- Now we can create package that can be given to others. --------------------------------- $ make dist CHECK dist-gzip MKDIR myhello-1.0 COPY myhello-1.0 PACK myhello-1.0.tar.gz $ ls Makefile antimake.mk hello hello.c inst myhello-1.0.tar.gz $ tar tzf myhello-1.0.tar.gz | sort myhello-1.0/ myhello-1.0/Makefile myhello-1.0/antimake.mk myhello-1.0/hello.c --------------------------------- Clean the tree --------------------------------- $ make clean CLEAN hello $ ls Makefile antimake.mk hello.c inst myhello-1.0.tar.gz --------------------------------- Done! pgqd/lib/mk/temos/expected/antimake3.txt0000664000401600040160000001405313175113172016601 0ustar cbecbe = Test Antimake EMBED_SUBDIRS = Antimake variable `EMBED_SUBDIRS` list names of directories that contains Makefile fragmants that are to be embedded into current Makefile. - Plus: Proper dependencies, work well with parallel Make. - Minus: Cannot go into subdir and run make there. - Minus: Fragments are not stand-alone, so need some care when writing. == Intro to EMBED_SUBDIRS == To better understand what EMBED_SUBDIRS does, let\'s start with simple case - single Makefile that references files under subdir: --------------------------------- $ mkdir -p src $ cp ../../antimake.mk . --------------------------------- .File: Makefile [source,makefile] ----------------------------------- bin_PROGRAMS = src/myprog src_myprog_SOURCES = src/myprog.c include antimake.mk ----------------------------------- .File: src/myprog.c [source,c] ----------------------------------- #include int main(void) { printf("myprog\n"); return 0; } ----------------------------------- --------------------------------- $ make CC src/myprog.c CCLD src/myprog $ ./src/myprog myprog $ make clean CLEAN src/myprog --------------------------------- Now you can put the lines that reference files under `src/` also into `src` and include that from top-level Makefile: .File: src/Makefile.inc ----------------------------------- bin_PROGRAMS = src/myprog src_myprog_SOURCES = src/myprog.c ----------------------------------- .File: Makefile [source,makefile] ----------------------------------- include src/Makefile.inc include antimake.mk ----------------------------------- --------------------------------- $ make CC src/myprog.c CCLD src/myprog $ ./src/myprog myprog --------------------------------- This works but the problem is that although the Makefile is local, it still sees files from top-Makefile-level. So that is what `EMBED_SUBDIRS` helps with - it allow to use local filenames in Makefile fragment, and it converts them to top-level filenames when including. It knows only few type of variables it needs to convert: - target filenames in primares lists (*_PROGRAMS, *_LIBRARIES, etc) - target_SOURCES: foo_SOURCES -> sub_dir_foo_SOURCES with filename conversion - other target variables: `foo_*` -> `sub_dir_foo_*` without filename conversion - EXTRA_DIST, CLEANFILES, DISTCLEANFILES, MAINTAINERCLEANFILES Any other variables stay untouched, and obviously they can mess up top-level variables. So the included Makefile should be as clean as possible. == Setup source tree for EMBED_SUBDIRS == Setup directories, install Antimake --------------------------------- $ mkdir -p lib1/sublib lib2 $ cp ../../antimake.mk . --------------------------------- Prepare sources .File: main.c [source,c] ----------------------------------- #include void func1(void); void func2(void); void func3(void); int main(void) { func1(); func2(); func3(); printf("main\n"); return 0; } ----------------------------------- .File: lib1/func1.c [source,c] ----------------------------------- #include void func1(void) { printf("func1\n"); } ----------------------------------- .File: lib1/sublib/func2.c [source,c] ----------------------------------- #include void func2(void) { printf("func2\n"); } ----------------------------------- .File: lib2/func3.c [source,c] ----------------------------------- #include void func3(void) { printf("func3\n"); } ----------------------------------- Prepare Makefiles .File: Makefile [source,makefile] ----------------------------------- PACKAGE_NAME = test-subdirs PACKAGE_VERSION = 1.0 EMBED_SUBDIRS = lib1 lib1/sublib lib2 bin_PROGRAMS = prog prog_SOURCES = main.c prog_LDADD = lib1/func1.a lib1/sublib/func2.a lib2/func3.a EXTRA_DIST = Makefile antimake.mk include antimake.mk ----------------------------------- .File: lib1/Makefile.am ----------------------------------- noinst_LIBRARIES = func1.a func1_a_SOURCES = func1.c EXTRA_DIST = Makefile.am ----------------------------------- .File: lib1/sublib/Makefile.am ----------------------------------- noinst_LIBRARIES = func2.a func2_a_SOURCES = func2.c EXTRA_DIST = Makefile.am ----------------------------------- .File: lib2/Makefile.am ----------------------------------- noinst_LIBRARIES = func3.a func3_a_SOURCES = func3.c EXTRA_DIST = Makefile.am ----------------------------------- == Building == Build the project --------------------------------- $ make CC main.c CC lib1/func1.c AR lib1/func1.a RANLIB lib1/func1.a CC lib1/sublib/func2.c AR lib1/sublib/func2.a RANLIB lib1/sublib/func2.a CC lib2/func3.c AR lib2/func3.a RANLIB lib2/func3.a CCLD prog $ ls Makefile antimake.mk lib1 lib2 main.c prog src $ ./prog func1 func2 func3 main --------------------------------- We can now install it: --------------------------------- $ make install prefix=/opt DESTDIR=./inst INSTALL prog ./inst/opt/bin $ ls ./inst/opt/bin prog --------------------------------- Now we can create package that can be given to others. --------------------------------- $ make dist CHECK dist-gzip MKDIR test-subdirs-1.0 COPY test-subdirs-1.0 PACK test-subdirs-1.0.tar.gz $ ls Makefile inst lib2 prog test-subdirs-1.0.tar.gz antimake.mk lib1 main.c src $ tar tzf test-subdirs-1.0.tar.gz | sort test-subdirs-1.0/ test-subdirs-1.0/Makefile test-subdirs-1.0/antimake.mk test-subdirs-1.0/lib1/ test-subdirs-1.0/lib1/Makefile.am test-subdirs-1.0/lib1/func1.c test-subdirs-1.0/lib1/sublib/ test-subdirs-1.0/lib1/sublib/Makefile.am test-subdirs-1.0/lib1/sublib/func2.c test-subdirs-1.0/lib2/ test-subdirs-1.0/lib2/Makefile.am test-subdirs-1.0/lib2/func3.c test-subdirs-1.0/main.c --------------------------------- Clean the tree --------------------------------- $ make clean CLEAN prog CLEAN lib1/func1.a CLEAN lib1/sublib/func2.a CLEAN lib2/func3.a CLEAN clean $ ls Makefile antimake.mk inst lib1 lib2 main.c src test-subdirs-1.0.tar.gz --------------------------------- Done! pgqd/lib/mk/temos/expected/libusual2.txt0000664000401600040160000000376213175113172016634 0ustar cbecbe = Embedding libusual as part of the source. = Here we build libusual as part of top-level tree. This gives the advantage of building only the modules that are actually used in main tree and without the intermediate `libusual.a` step. This method is for projects that are developed in parallel with libusual. Not recommended for casual usage. == Configure libusual == Here we configure libusual, but do not build it. --------------------------------- $ git clone git://github.com/libusual/libusual.git lib Cloning into 'lib'... done. $ cd lib $ ./autogen.sh [...] $ ./configure [...] $ cd .. --------------------------------- == Prepare own code == Here is the source that needs to be linked with libusual: .File: prog.c [source,c] ----------------------------------- #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } ----------------------------------- == Build with Antimake. == Antimake is build framework on plain GNU Make that reads build instructons with Automake syntax. It has also hooks for libusual integration. Here is Makefile that uses Antimake: .File: Makefile [source,makefile] ----------------------------------- # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c # location of configured libusual USUAL_DIR = lib # mention that 'prog' wants embedded libusual prog_EMBED_LIBUSUAL = 1 # Load Antimake plugin that handles libusual embedding AM_FEATURES = libusual # launch Antimake include $(USUAL_DIR)/mk/antimake.mk ----------------------------------- Build the project --------------------------------- $ make CC prog.c CC lib/usual/hashing/crc32.c CC lib/usual/base.c CCLD prog $ ls Makefile lib prog prog.c $ ./prog crc: 12345678 $ make clean CLEAN prog $ ls Makefile lib prog.c --------------------------------- Done pgqd/lib/mk/temos/expected/libusual3.txt0000664000401600040160000000501213175113172016623 0ustar cbecbe = Using Autoconf and embedded libusual = == Fetch libusual == Here we close libusual repo, but do not configure nor build it. --------------------------------- $ git clone git://github.com/libusual/libusual.git lib Cloning into 'lib'... done. --------------------------------- Autoconf setup .File: autogen.sh [source,shell] ----------------------------------- # use prepared autgen logic ./lib/mk/std-autogen.sh ./lib # fetch Antimake template from libusual cp lib/mk/antimake.mk antimake.mk.in ----------------------------------- .File: configure.ac [source,autoconf] ----------------------------------- AC_INIT([achello], [0.1], [https://libusual.github.com]) AC_CONFIG_SRCDIR([prog.c]) AC_CONFIG_HEADER([lib/usual/config.h]) AC_PREREQ([2.59]) AC_USUAL_PORT_CHECK AC_USUAL_PROGRAM_CHECK AC_USUAL_HEADER_CHECK AC_USUAL_TYPE_CHECK AC_USUAL_FUNCTION_CHECK AC_OUTPUT([antimake.mk]) ----------------------------------- Here is the source that needs to be linked with libusual: .File: prog.c [source,c] ----------------------------------- #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } ----------------------------------- Antimake based Makefile .File: Makefile [source,makefile] ----------------------------------- # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c # location of configured libusual USUAL_DIR = lib # mention that 'prog' wants embedded libusual prog_EMBED_LIBUSUAL = 1 AM_FEATURES = libusual # clean configured files DISTCLEANFILES = config.status config.log \ antimake.mk $(USUAL_DIR)/usual/config.h # clean generated files MAINTAINERCLEANFILES = configure config.guess config.sub install-sh \ antimake.mk.in $(USUAL_DIR)/usual/config.h.in # launch Antimake include $(USUAL_DIR)/mk/antimake.mk ----------------------------------- Build the project --------------------------------- $ sh ./autogen.sh $ ./configure [...] $ make CC prog.c CC lib/usual/hashing/crc32.c CC lib/usual/base.c CCLD prog $ ls Makefile autogen.sh config.status configure.ac prog antimake.mk config.guess config.sub install-sh prog.c antimake.mk.in config.log configure lib $ ./prog crc: 12345678 $ make maintainer-clean CLEAN prog MAINTAINERCLEAN maintainer-clean $ ls Makefile autogen.sh configure.ac lib prog.c --------------------------------- Done pgqd/lib/mk/temos/expected/antimake4.txt0000664000401600040160000000510313175113172016576 0ustar cbecbe = Using Antimake with autoconf = Autoconf setup .File: autogen.sh [source,shell] ----------------------------------- ../../std-autogen.sh ../../.. # fetch Antimake template from libusual cp ../../antimake.mk antimake.mk.in ----------------------------------- .File: configure.ac [source,autoconf] ----------------------------------- AC_INIT([actest], [0.1]) AC_CONFIG_SRCDIR([prog.c]) AC_PREREQ([2.59]) AC_USUAL_INIT AC_USUAL_PROGRAM_CHECK AC_OUTPUT([antimake.mk]) ----------------------------------- Here is the source we want to build: .File: prog.c [source,c] ----------------------------------- #include #include int main(void) { printf("hello\n"); return 0; } ----------------------------------- Antimake based Makefile .File: Makefile [source,makefile] ----------------------------------- # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c EXTRA_DIST = Makefile $(MAINTAINERCLEANFILES) # clean configured files DISTCLEANFILES = config.status config.log antimake.mk # clean generated files MAINTAINERCLEANFILES = configure config.guess config.sub install-sh antimake.mk.in # launch Antimake include antimake.mk ----------------------------------- Build the project --------------------------------- $ sh ./autogen.sh $ ./configure [...] $ make CC prog.c CCLD prog $ ls Makefile autogen.sh config.status configure.ac prog.c antimake.mk config.guess config.sub install-sh antimake.mk.in config.log configure prog $ ./prog hello --------------------------------- Create distribution package --------------------------------- $ make dist CHECK dist-gzip MKDIR actest-0.1 COPY actest-0.1 PACK actest-0.1.tar.gz $ tar tzf actest-0.1.tar.gz | sort actest-0.1/ actest-0.1/Makefile actest-0.1/antimake.mk.in actest-0.1/config.guess actest-0.1/config.sub actest-0.1/configure actest-0.1/install-sh actest-0.1/prog.c --------------------------------- Test the distribution package and separate build dir --------------------------------- $ mkdir -p test $ cd test $ tar xf ../actest-0.1.tar.gz $ mkdir build $ cd build $ ../actest-0.1/configure [...] $ make CC ../actest-0.1/prog.c CCLD prog $ ls Makefile antimake.mk config.log config.status prog $ ./prog hello $ cd ../.. --------------------------------- Clean up --------------------------------- $ make maintainer-clean CLEAN prog MAINTAINERCLEAN maintainer-clean $ ls Makefile actest-0.1.tar.gz autogen.sh configure.ac prog.c test --------------------------------- Done pgqd/lib/mk/temos/expected/antimake5.txt0000664000401600040160000001001613175113172016576 0ustar cbecbe = Shared libraries and autoconf = Autoconf setup .File: autogen.sh [source,shell] ----------------------------------- ../../std-autogen.sh ../../.. # fetch Antimake template from libusual cp ../../antimake.mk antimake.mk.in ----------------------------------- .File: configure.ac [source,autoconf] ----------------------------------- AC_INIT([actest], [0.1]) AC_CONFIG_SRCDIR([prog.c]) AC_PREREQ([2.59]) LT_INIT AC_USUAL_INIT AC_USUAL_PROGRAM_CHECK AC_OUTPUT([antimake.mk]) ----------------------------------- Here are the source files: .File: prog.c [source,c] ----------------------------------- void func1(void); int main(void) { func1(); return 0; } ----------------------------------- .File: func.c [source,c] ----------------------------------- #include void func1(void); void func1(void) { printf("hello from func1\n"); } ----------------------------------- Antimake based Makefile .File: Makefile [source,makefile] ----------------------------------- lib_LTLIBRARIES = libtemo.la libtemo_la_SOURCES = func.c libtemo_la_LDFLAGS = -version-info 3:0:2 bin_PROGRAMS = prog prog_SOURCES = prog.c prog_LDADD = libtemo.la # clean configured files DISTCLEANFILES = \ config.status \ config.log \ antimake.mk \ libtool # clean generated files MAINTAINERCLEANFILES = \ configure \ config.guess \ config.sub \ install-sh \ antimake.mk.in \ ltmain.sh EXTRA_DIST = \ Makefile \ $(MAINTAINERCLEANFILES) # launch Antimake include antimake.mk ----------------------------------- Build the project --------------------------------- $ sh ./autogen.sh $ ./configure [...] $ make CC prog.c CC func.c CCLD libtemo.la CCLD prog $ ls Makefile autogen.sh config.status configure.ac libtemo.la prog antimake.mk config.guess config.sub func.c libtool prog.c antimake.mk.in config.log configure install-sh ltmain.sh $ ./prog hello from func1 --------------------------------- Create distribution package --------------------------------- $ make dist CHECK dist-gzip MKDIR actest-0.1 COPY actest-0.1 PACK actest-0.1.tar.gz $ tar tzf actest-0.1.tar.gz | sort actest-0.1/ actest-0.1/Makefile actest-0.1/antimake.mk.in actest-0.1/config.guess actest-0.1/config.sub actest-0.1/configure actest-0.1/func.c actest-0.1/install-sh actest-0.1/ltmain.sh actest-0.1/prog.c --------------------------------- Test installation --------------------------------- $ make install DESTDIR=/tmp/test-inst INSTALL prog /tmp/test-inst/usr/local/bin INSTALL libtemo.la /tmp/test-inst/usr/local/lib libtool: install: warning: remember to run `libtool --finish /usr/local/lib' $ ls Makefile autogen.sh config.sub install-sh prog actest-0.1.tar.gz config.guess configure libtemo.la prog.c antimake.mk config.log configure.ac libtool antimake.mk.in config.status func.c ltmain.sh $ find /tmp/test-inst | sort /tmp/test-inst /tmp/test-inst/usr /tmp/test-inst/usr/local /tmp/test-inst/usr/local/bin /tmp/test-inst/usr/local/bin/prog /tmp/test-inst/usr/local/lib /tmp/test-inst/usr/local/lib/libtemo.a /tmp/test-inst/usr/local/lib/libtemo.la /tmp/test-inst/usr/local/lib/libtemo.so /tmp/test-inst/usr/local/lib/libtemo.so.1 /tmp/test-inst/usr/local/lib/libtemo.so.1.2.0 $ rm -rf /tmp/test-inst --------------------------------- Test the distribution package and separate build dir --------------------------------- $ mkdir -p test $ cd test $ tar xf ../actest-0.1.tar.gz $ mkdir build $ cd build $ ../actest-0.1/configure [...] $ make CC ../actest-0.1/prog.c CC ../actest-0.1/func.c CCLD libtemo.la CCLD prog $ ls Makefile antimake.mk config.log config.status libtemo.la libtool prog $ ./prog hello from func1 $ cd ../.. --------------------------------- Clean up --------------------------------- $ make maintainer-clean CLEAN prog CLEAN libtemo.la MAINTAINERCLEAN maintainer-clean $ ls Makefile actest-0.1.tar.gz autogen.sh configure.ac func.c prog.c test --------------------------------- Done pgqd/lib/mk/temos/expected/antimake2.txt0000664000401600040160000001257713175113172016611 0ustar cbecbe = Test Antimake SUBDIRS = Antimake variable `SUBDIRS` list names of directories that the Make needs to recurse into. Each of them contains stand-alone Makefile that directs building in that subdirs. - Plus: you can call 'make' while being in subdir to build only local targets. - Minus: dependencies between subdirs do not work. == Setup source tree == Setup directories, install Antimake --------------------------------- $ mkdir -p lib1/sublib lib2 $ cp ../../antimake.mk . --------------------------------- Prepare sources .File: api.h [source,c] ----------------------------------- void func1(void); void func2(void); void func3(void); ----------------------------------- .File: main.c [source,c] ----------------------------------- #include #include "api.h" int main(void) { func1(); func2(); func3(); printf("main\n"); return 0; } ----------------------------------- .File: lib1/func1.c [source,c] ----------------------------------- #include #include "api.h" void func1(void) { printf("func1\n"); } ----------------------------------- .File: lib1/sublib/func2.c [source,c] ----------------------------------- #include #include "api.h" void func2(void) { printf("func2\n"); } ----------------------------------- .File: lib2/func3.c [source,c] ----------------------------------- #include #include "api.h" void func3(void) { printf("func3\n"); } ----------------------------------- Prepare Makefiles .File: Makefile [source,makefile] ----------------------------------- PACKAGE_NAME = test-subdirs PACKAGE_VERSION = 1.0 SUBDIRS = lib1 lib2 bin_PROGRAMS = prog prog_SOURCES = main.c prog_LDADD = lib1/func1.a lib1/sublib/func2.a lib2/func3.a EXTRA_DIST = Makefile antimake.mk include antimake.mk ----------------------------------- .File: lib1/Makefile [source,makefile] ----------------------------------- SUBLOC = lib1 SUBDIRS = sublib noinst_LIBRARIES = func1.a func1_a_SOURCES = func1.c func1_a_CPPFLAGS = -I.. EXTRA_DIST = Makefile include ../antimake.mk ----------------------------------- .File: lib1/sublib/Makefile [source,makefile] ----------------------------------- SUBLOC = lib1/sublib noinst_LIBRARIES = func2.a func2_a_SOURCES = func2.c func2_a_CPPFLAGS = -I../.. EXTRA_DIST = Makefile include ../../antimake.mk ----------------------------------- .File: lib2/Makefile [source,makefile] ----------------------------------- SUBLOC = lib2 noinst_LIBRARIES = func3.a func3_a_SOURCES = func3.c func3_a_CPPFLAGS = -I$(top_srcdir) EXTRA_DIST = Makefile include ../antimake.mk ----------------------------------- == Building == Build the project --------------------------------- $ make --> lib1 --> lib1/sublib CC func2.c AR func2.a RANLIB func2.a <-- lib1/sublib CC func1.c AR func1.a RANLIB func1.a <-- lib1 --> lib2 CC func3.c AR func3.a RANLIB func3.a <-- lib2 CC main.c CCLD prog $ ls Makefile antimake.mk api.h lib1 lib2 main.c prog $ ./prog func1 func2 func3 main --------------------------------- We can now install it: --------------------------------- $ make install DESTDIR=./inst --> lib1 --> lib1/sublib make[2]: Nothing to be done for `install'. <-- lib1/sublib <-- lib1 --> lib2 make[1]: Nothing to be done for `install'. <-- lib2 INSTALL prog ./inst/usr/local/bin $ ls ./inst/usr/local/bin prog --------------------------------- Now we can create package that can be given to others. --------------------------------- $ make dist CHECK dist-gzip MKDIR test-subdirs-1.0 COPY test-subdirs-1.0 PACK test-subdirs-1.0.tar.gz $ ls Makefile api.h lib1 main.c test-subdirs-1.0.tar.gz antimake.mk inst lib2 prog $ tar tzf test-subdirs-1.0.tar.gz | sort test-subdirs-1.0/ test-subdirs-1.0/Makefile test-subdirs-1.0/antimake.mk test-subdirs-1.0/lib1/ test-subdirs-1.0/lib1/Makefile test-subdirs-1.0/lib1/func1.c test-subdirs-1.0/lib1/sublib/ test-subdirs-1.0/lib1/sublib/Makefile test-subdirs-1.0/lib1/sublib/func2.c test-subdirs-1.0/lib2/ test-subdirs-1.0/lib2/Makefile test-subdirs-1.0/lib2/func3.c test-subdirs-1.0/main.c --------------------------------- Clean the tree --------------------------------- $ make clean --> lib1 --> lib1/sublib CLEAN func2.a <-- lib1/sublib CLEAN func1.a <-- lib1 --> lib2 CLEAN func3.a <-- lib2 CLEAN prog $ ls Makefile antimake.mk api.h inst lib1 lib2 main.c test-subdirs-1.0.tar.gz --------------------------------- Test O= --------------------------------- $ mkdir -p build $ make O=build MKDIR Create lib1 --> lib1 MKDIR Create lib1/sublib --> lib1/sublib CC ../../../lib1/sublib/func2.c AR func2.a RANLIB func2.a <-- lib1/sublib CC ../../lib1/func1.c AR func1.a RANLIB func1.a <-- lib1 MKDIR Create lib2 --> lib2 CC ../../lib2/func3.c AR func3.a RANLIB func3.a <-- lib2 CC ../main.c CCLD prog $ ls Makefile api.h inst lib2 test-subdirs-1.0.tar.gz antimake.mk build lib1 main.c $ ls build Makefile antimake.mk lib1 lib2 prog --------------------------------- Done! pgqd/lib/mk/temos/expected/libusual4.txt0000664000401600040160000000773613175113172016643 0ustar cbecbe = Use installed libusual = Install libusual and link against it. == Build libusual == --------------------------------- $ git clone git://github.com/libusual/libusual.git libusual Cloning into 'libusual'... done. $ cd libusual $ ./autogen.sh [...] $ ./configure --disable-static --prefix=`pwd`/../inst [...] $ make [...] $ make install [...] $ cd .. $ find inst | sort inst inst/include inst/include/usual inst/include/usual/aatree.h inst/include/usual/base.h inst/include/usual/base_win32.h inst/include/usual/bits.h inst/include/usual/cbtree.h inst/include/usual/cfparser.h inst/include/usual/config.h inst/include/usual/config_msvc.h inst/include/usual/crypto inst/include/usual/crypto/digest.h inst/include/usual/crypto/hmac.h inst/include/usual/crypto/keccak.h inst/include/usual/crypto/md5.h inst/include/usual/crypto/sha1.h inst/include/usual/crypto/sha256.h inst/include/usual/crypto/sha512.h inst/include/usual/ctype.h inst/include/usual/cxalloc.h inst/include/usual/cxextra.h inst/include/usual/daemon.h inst/include/usual/endian.h inst/include/usual/err.h inst/include/usual/event.h inst/include/usual/fileutil.h inst/include/usual/fnmatch.h inst/include/usual/getopt.h inst/include/usual/hashing inst/include/usual/hashing/crc32.h inst/include/usual/hashing/lookup3.h inst/include/usual/hashing/siphash.h inst/include/usual/hashtab-impl.h inst/include/usual/heap.h inst/include/usual/list.h inst/include/usual/logging.h inst/include/usual/mbuf.h inst/include/usual/mdict.h inst/include/usual/mempool.h inst/include/usual/misc.h inst/include/usual/netdb.h inst/include/usual/pgutil.h inst/include/usual/pgutil_kwlookup.h inst/include/usual/pthread.h inst/include/usual/regex.h inst/include/usual/safeio.h inst/include/usual/shlist.h inst/include/usual/signal.h inst/include/usual/slab.h inst/include/usual/socket.h inst/include/usual/socket_win32.h inst/include/usual/statlist.h inst/include/usual/string.h inst/include/usual/strpool.h inst/include/usual/time.h inst/include/usual/utf8.h inst/include/usual/wchar.h inst/lib inst/lib/libusual.a inst/lib/pkgconfig inst/lib/pkgconfig/libusual.pc inst/share inst/share/aclocal inst/share/aclocal/antimake.m4 inst/share/aclocal/usual.m4 inst/share/libusual inst/share/libusual/amext-cxx.mk inst/share/libusual/amext-libusual.mk inst/share/libusual/amext-modes.mk inst/share/libusual/amext-msvc.mk inst/share/libusual/antimake.mk inst/share/libusual/find_modules.sh inst/share/libusual/std-autogen.sh --------------------------------- == Build our own code == Now we prepare our own code. First, this is the source file: .File: prog.c [source,c] ----------------------------------- #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } ----------------------------------- Here is corresponding Makefile: .File: Makefile [source,makefile] ----------------------------------- CC = gcc CFLAGS = -O -g -Wall # here we describe our program SRCS = prog.c OBJS = $(SRCS:.c=.o) # put libusual flags to proper place CPPFLAGS = $(USUAL_CPPFLAGS) LIBS = $(USUAL_LIBS) # use pkg-config to get libusual info USUAL_CPPFLAGS = $(shell $(PKG_CONFIG) --cflags libusual) USUAL_LIBS = $(shell $(PKG_CONFIG) --libs libusual) # temo hacks to support local install, not needed otherwise PKG_CONFIG := PKG_CONFIG_PATH=$(CURDIR)/inst/lib/pkgconfig pkg-config CPPFLAGS := $(subst $(CURDIR)/libusual/../,./,$(CPPFLAGS)) LIBS := $(subst $(CURDIR)/libusual/../,./,$(LIBS)) all: prog %.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< prog: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ ----------------------------------- Build the project --------------------------------- $ make gcc -O -g -Wall -I./inst/include -c -o prog.o prog.c gcc -O -g -Wall prog.o -L./inst/lib -lusual -o prog $ ls Makefile inst libusual prog prog.c prog.o $ LD_LIBRARY_PATH=./inst/lib ./prog crc: 12345678 --------------------------------- Done! pgqd/lib/mk/temos/expected/libusual6.txt0000664000401600040160000000657413175113172016644 0ustar cbecbe = Use installed libusual with autoconf and antimake. = Install libusual and link against it. == Build libusual == --------------------------------- $ git clone git://github.com/libusual/libusual.git libusual Cloning into 'libusual'... done. $ cd libusual $ ./autogen.sh [...] $ ./configure --disable-shared --prefix=`pwd`/../inst [...] $ make [...] $ make install [...] $ cd .. --------------------------------- == Build our own code == Now we prepare our own code. First, this is the source file: .File: prog.c [source,c] ----------------------------------- #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } ----------------------------------- Autoconf setup .File: autogen.sh [source,shell] ----------------------------------- # use prepared autgen logic ../../std-autogen.sh ../../.. # fetch Antimake template from libusual cp ../../antimake.mk antimake.mk.in ----------------------------------- .File: extra.mk.in ----------------------------------- USUAL_ANTIMAKE = @USUAL_ANTIMAKE@ USUAL_CFLAGS = @USUAL_CFLAGS@ USUAL_LIBS = @USUAL_LIBS@ ----------------------------------- .File: configure.ac [source,autoconf] ----------------------------------- AC_INIT([achello], [0.1], [https://libusual.github.com]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_SRCDIR([prog.c]) AC_PREREQ([2.59]) AC_USUAL_INIT AC_USUAL_PROGRAM_CHECK PKG_CHECK_MODULES(USUAL, libusual) _PKG_CONFIG([USUAL_ANTIMAKE], [variable=antimake], [libusual]) AC_SUBST([USUAL_ANTIMAKE]) AC_OUTPUT([antimake.mk extra.mk]) ----------------------------------- Here is the source that needs to be linked with libusual: .File: prog.c [source,c] ----------------------------------- #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } ----------------------------------- Antimake based Makefile .File: Makefile [source,makefile] ----------------------------------- # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c prog_CPPFLAGS = $(USUAL_CFLAGS) prog_LDADD = $(USUAL_LIBS) # clean configured files DISTCLEANFILES = config.status config.log extra.mk antimake.mk config.h # clean generated files MAINTAINERCLEANFILES = configure config.guess config.sub install-sh \ antimake.mk.in extra.mk.in config.h.in EXTRA_DIST = Makefile $(MAINTAINERCLEANFILES) # launch Antimake include extra.mk include antimake.mk ----------------------------------- Build the project --------------------------------- $ sh ./autogen.sh $ PKG_CONFIG_PATH=`pwd`/inst/lib/pkgconfig ./configure [...] $ make CC prog.c CCLD prog $ ls Makefile config.guess config.status extra.mk libusual antimake.mk config.h config.sub extra.mk.in prog antimake.mk.in config.h.in configure inst prog.c autogen.sh config.log configure.ac install-sh $ ./prog crc: 12345678 $ make am-show-distfiles Makefile antimake.mk.in config.guess config.h.in config.sub configure extra.mk.in install-sh prog.c $ make maintainer-clean CLEAN prog MAINTAINERCLEAN maintainer-clean $ ls Makefile autogen.sh configure.ac inst libusual prog.c --------------------------------- Done! pgqd/lib/mk/temos/expected/antimake6.txt0000664000401600040160000002367113175113172016612 0ustar cbecbe = Antimake stress-test = Autoconf setup .File: autogen.sh [source,shell] ----------------------------------- ../../std-autogen.sh ../../.. # fetch Antimake template from libusual make -f ../../antimake.mk show-config > build.mk.in echo 'include $(abs_top_srcdir)/antimake.mk' >> build.mk.in ln -sf ../../antimake.mk . ln -sf ../../amext-cxx.mk . ----------------------------------- .File: configure.ac [source,autoconf] ----------------------------------- AC_INIT([actest], [0.1]) AC_CONFIG_SRCDIR([esub/prog.c]) AC_PREREQ([2.59]) AC_USUAL_INIT LT_INIT AC_USUAL_PROGRAM_CHECK AC_PROG_CXX AC_OUTPUT([build.mk]) ----------------------------------- Here are the source files: ------------- ./prog.c ./cpptest.cpp ./sub/func1.c ./sub/esub/func2,c ./sub/sub/func3.c ./esub/func4.c ./esub/esub/func5.c ./esub/sub/func6.c ------------- --------------------------------- $ mkdir -p sub/esub sub/sub esub/esub esub/sub --------------------------------- .File: esub/prog.c [source,c] ----------------------------------- #include "func1.h" #include "func2.h" #include "func3.h" #include "func4.h" #include "func5.h" #include "func6.h" #include int main(void) { printf("%s\n", __FILE__); func1(); func2(); func3(); func4(); func5(); func6(); return 0; } ----------------------------------- .File: cpptest.cpp ----------------------------------- #include using namespace std; int main(void) { cout << "Hello" << endl; return 0; } ----------------------------------- .File: sub/func1.c [source,c] ----------------------------------- #include #include "func1.h" void func1(void) { printf("%s\n", __FILE__); } ----------------------------------- .File: sub/func1.h [source,c] ----------------------------------- void func1(void); ----------------------------------- --------------------------------- $ sed s/1/2/ sub/func1.c > sub/esub/func2.c $ sed s/1/2/ sub/func1.h > sub/esub/func2.h $ sed s/1/3/ sub/func1.c > sub/sub/func3.c $ sed s/1/3/ sub/func1.h > sub/sub/func3.h $ sed s/1/4/ sub/func1.c > esub/func4.c $ sed s/1/4/ sub/func1.h > esub/func4.h $ sed s/1/5/ sub/func1.c > esub/sub/func5.c $ sed s/1/5/ sub/func1.h > esub/sub/func5.h $ sed s/1/6/ sub/func1.c > esub/esub/func6.c $ sed s/1/6/ sub/func1.h > esub/esub/func6.h --------------------------------- Now fill makefiles .File: Makefile [source,makefile] ----------------------------------- SUBDIRS = sub EMBED_SUBDIRS = esub AM_FEATURES = cxx override WFLAGS = -Wall EXTRA_DIST = Makefile antimake.mk amext-cxx.mk $(MAINTAINERCLEANFILES) # clean configured files DISTCLEANFILES = \ config.status \ config.log \ libtool # clean generated files MAINTAINERCLEANFILES = \ configure \ config.guess \ config.sub \ install-sh \ build.mk.in \ ltmain.sh noinst_PROGRAMS = cpptest cpptest_SOURCES = cpptest.cpp # launch Antimake include build.mk ----------------------------------- .File: sub/Makefile [source,makefile] ----------------------------------- SUBLOC = sub SUBDIRS = sub EMBED_SUBDIRS = esub noinst_LIBRARIES = libfunc1.a libfunc1_a_SOURCES = func1.c func1.h EXTRA_DIST = Makefile include ../build.mk ----------------------------------- .File: sub/sub/Makefile [source,makefile] ----------------------------------- SUBLOC = sub/sub EXTRA_DIST = Makefile noinst_LIBRARIES = libfunc3.a libfunc3_a_SOURCES = func3.c func3.h include ../../build.mk ----------------------------------- .File: sub/esub/Makefile.am ----------------------------------- noinst_LIBRARIES = libfunc2.a libfunc2_a_SOURCES = func2.c func2.h EXTRA_DIST = Makefile.am ----------------------------------- .File: esub/Makefile.am ----------------------------------- SUBDIRS = sub EMBED_SUBDIRS = esub EXTRA_DIST = Makefile.am noinst_LIBRARIES = libfunc4.a libfunc4_a_SOURCES = func4.c func4.h noinst_PROGRAMS = prog prog_SOURCES = prog.c prog_LDFLAGS = -L../sub -L../sub/esub -L. -Lsub prog_LDADD = \ -lfunc1 \ -lfunc2 \ $(topdir)/sub/sub/libfunc3.a \ -lfunc4 \ -lfunc5 \ esub/libfunc6.a prog_CFLAGS = -I../sub prog_CPPFLAGS = \ -I../sub/esub \ -I$(topdir)/sub/sub \ -I. \ -Iesub \ -I./sub ----------------------------------- .File: esub/sub/Makefile [source,makefile] ----------------------------------- SUBLOC = esub/sub EXTRA_DIST = Makefile noinst_LIBRARIES = libfunc5.a libfunc5_a_SOURCES = func5.c func5.h include ../../build.mk ----------------------------------- .File: esub/esub/Makefile.am ----------------------------------- EXTRA_DIST = Makefile.am noinst_LIBRARIES = libfunc6.a libfunc6_a_SOURCES = func6.c func6.h ----------------------------------- Build the project --------------------------------- $ sh ./autogen.sh $ ./configure [...] $ make --> sub --> sub/sub CC func3.c AR libfunc3.a RANLIB libfunc3.a <-- sub/sub CC func1.c AR libfunc1.a RANLIB libfunc1.a CC esub/func2.c AR esub/libfunc2.a RANLIB esub/libfunc2.a <-- sub --> esub/sub CC func5.c AR libfunc5.a RANLIB libfunc5.a <-- esub/sub CC esub/func4.c AR esub/libfunc4.a RANLIB esub/libfunc4.a CC esub/esub/func6.c AR esub/esub/libfunc6.a RANLIB esub/esub/libfunc6.a CXX cpptest.cpp CXXLD cpptest CC esub/prog.c CCLD esub/prog $ ls Makefile build.mk config.status cpptest libtool amext-cxx.mk build.mk.in config.sub cpptest.cpp ltmain.sh antimake.mk config.guess configure esub sub autogen.sh config.log configure.ac install-sh $ ./esub/prog esub/prog.c func1.c esub/func2.c func3.c esub/func4.c func5.c esub/esub/func6.c --------------------------------- Create distribution package --------------------------------- $ make dist CHECK dist-gzip MKDIR actest-0.1 COPY actest-0.1 PACK actest-0.1.tar.gz $ tar tzf actest-0.1.tar.gz | sort actest-0.1/ actest-0.1/Makefile actest-0.1/amext-cxx.mk actest-0.1/antimake.mk actest-0.1/build.mk.in actest-0.1/config.guess actest-0.1/config.sub actest-0.1/configure actest-0.1/cpptest.cpp actest-0.1/esub/ actest-0.1/esub/Makefile.am actest-0.1/esub/esub/ actest-0.1/esub/esub/Makefile.am actest-0.1/esub/esub/func6.c actest-0.1/esub/esub/func6.h actest-0.1/esub/func4.c actest-0.1/esub/func4.h actest-0.1/esub/prog.c actest-0.1/esub/sub/ actest-0.1/esub/sub/Makefile actest-0.1/esub/sub/func5.c actest-0.1/esub/sub/func5.h actest-0.1/install-sh actest-0.1/ltmain.sh actest-0.1/sub/ actest-0.1/sub/Makefile actest-0.1/sub/esub/ actest-0.1/sub/esub/Makefile.am actest-0.1/sub/esub/func2.c actest-0.1/sub/esub/func2.h actest-0.1/sub/func1.c actest-0.1/sub/func1.h actest-0.1/sub/sub/ actest-0.1/sub/sub/Makefile actest-0.1/sub/sub/func3.c actest-0.1/sub/sub/func3.h --------------------------------- Test installation --------------------------------- $ make install DESTDIR=`pwd`/inst --> sub --> sub/sub make[2]: Nothing to be done for `install'. <-- sub/sub <-- sub --> esub/sub make[1]: Nothing to be done for `install'. <-- esub/sub $ ls Makefile autogen.sh config.log configure.ac install-sh actest-0.1.tar.gz build.mk config.status cpptest libtool amext-cxx.mk build.mk.in config.sub cpptest.cpp ltmain.sh antimake.mk config.guess configure esub sub $ find inst | sort find: `inst': No such file or directory --------------------------------- Test the distribution package and separate build dir --------------------------------- $ mkdir -p test $ cd test $ tar xf ../actest-0.1.tar.gz $ mkdir build $ cd build $ ../actest-0.1/configure [...] $ make MKDIR Create sub --> sub MKDIR Create sub/sub --> sub/sub CC ../../../actest-0.1/sub/sub/func3.c AR libfunc3.a RANLIB libfunc3.a <-- sub/sub CC ../../actest-0.1/sub/func1.c AR libfunc1.a RANLIB libfunc1.a CC ../../actest-0.1/sub/esub/func2.c AR esub/libfunc2.a RANLIB esub/libfunc2.a <-- sub MKDIR Create esub/sub --> esub/sub CC ../../../actest-0.1/esub/sub/func5.c AR libfunc5.a RANLIB libfunc5.a <-- esub/sub CC ../actest-0.1/esub/func4.c AR esub/libfunc4.a RANLIB esub/libfunc4.a CC ../actest-0.1/esub/esub/func6.c AR esub/esub/libfunc6.a RANLIB esub/esub/libfunc6.a CXX ../actest-0.1/cpptest.cpp CXXLD cpptest CC ../actest-0.1/esub/prog.c CCLD esub/prog $ ls Makefile build.mk config.log config.status cpptest esub libtool sub $ make esub/prog make: `esub/prog' is up to date. $ ./esub/prog ../actest-0.1/esub/prog.c ../../actest-0.1/sub/func1.c ../../actest-0.1/sub/esub/func2.c ../../../actest-0.1/sub/sub/func3.c ../actest-0.1/esub/func4.c ../../../actest-0.1/esub/sub/func5.c ../actest-0.1/esub/esub/func6.c $ cd ../.. --------------------------------- Clean up --------------------------------- $ make maintainer-clean --> sub --> sub/sub CLEAN libfunc3.a <-- sub/sub CLEAN libfunc1.a CLEAN esub/libfunc2.a CLEAN clean <-- sub --> esub/sub CLEAN libfunc5.a <-- esub/sub CLEAN esub/libfunc4.a CLEAN esub/esub/libfunc6.a CLEAN cpptest CLEAN esub/prog CLEAN clean --> sub --> sub/sub CLEAN libfunc3.a <-- sub/sub CLEAN libfunc1.a CLEAN esub/libfunc2.a CLEAN clean --> sub/sub CLEAN libfunc3.a MAINTAINERCLEAN maintainer-clean <-- sub/sub MAINTAINERCLEAN maintainer-clean <-- sub --> esub/sub CLEAN libfunc5.a MAINTAINERCLEAN maintainer-clean <-- esub/sub MAINTAINERCLEAN maintainer-clean $ ls Makefile amext-cxx.mk autogen.sh configure.ac esub test actest-0.1.tar.gz antimake.mk build.mk cpptest.cpp sub --------------------------------- Done pgqd/lib/mk/temos/expected/libusual1.txt0000664000401600040160000000325313175113172016626 0ustar cbecbe = Use libusual the simplest way = Simplest usage would be to configure and build libusual locally and point your projects CPPFLAGS and LDFLAGS there. That way you get access to not only code but also various autoconfigued symbols without any complexities in your project. == Build libusual == --------------------------------- $ git clone git://github.com/libusual/libusual.git lib Cloning into 'lib'... done. $ cd lib $ ./autogen.sh [...] $ ./configure --disable-shared --prefix=/opt [...] $ make [...] $ make install DESTDIR=`pwd`/../inst [...] $ cd .. --------------------------------- == Build our own code == Now we prepare our own code. First, this is the source file: .File: prog.c [source,c] ----------------------------------- #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } ----------------------------------- Here is corresponding Makefile: .File: Makefile [source,makefile] ----------------------------------- # here we describe our program SRCS = prog.c OBJS = $(SRCS:.c=.o) # here we link to libusual CPPFLAGS = -I./inst/opt/include LDFLAGS = -L./inst/opt/lib LIBS = -lusual CC = gcc CFLAGS = -O -g -Wall all: prog prog: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ ----------------------------------- Build the project --------------------------------- $ make gcc -O -g -Wall -I./inst/opt/include -c -o prog.o prog.c gcc -O -g -Wall -L./inst/opt/lib prog.o -lusual -o prog $ ls Makefile inst lib prog prog.c prog.o $ ./prog crc: 12345678 --------------------------------- Done! pgqd/lib/mk/temos/expected/libusual5.txt0000664000401600040160000000335613175113172016636 0ustar cbecbe = Use installed libusual with antimake. = Install libusual and link against it. == Build libusual == --------------------------------- $ git clone git://github.com/libusual/libusual.git libusual Cloning into 'libusual'... done. $ cd libusual $ ./autogen.sh [...] $ ./configure --disable-shared --prefix=`pwd`/../inst [...] $ make [...] $ make install [...] $ cd .. --------------------------------- == Build our own code == Now we prepare our own code. First, this is the source file: .File: prog.c [source,c] ----------------------------------- #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } ----------------------------------- Here is corresponding Makefile: .File: Makefile [source,makefile] ----------------------------------- # temo hacks to support local install, not needed otherwise PKG_CONFIG = PKG_CONFIG_PATH=$(CURDIR)/inst/lib/pkgconfig pkg-config # use pkg-config to get libusual info USUAL_CPPFLAGS = $(shell $(PKG_CONFIG) libusual --cflags) USUAL_LDFLAGS = $(shell $(PKG_CONFIG) libusual --libs-only-L) USUAL_LIBS = $(shell $(PKG_CONFIG) libusual --libs-only-l) # Generic Antimake bin_PROGRAMS = prog prog_SOURCES = prog.c prog_CPPFLAGS = $(USUAL_CPPFLAGS) prog_LDFLAGS = $(USUAL_LDFLAGS) prog_LDADD = $(USUAL_LIBS) # use installed Antimake ANTIMAKE = $(shell $(PKG_CONFIG) libusual --variable=antimake) include $(ANTIMAKE) ----------------------------------- Build the project --------------------------------- $ make CC prog.c CCLD prog $ ls Makefile inst libusual prog prog.c $ ./prog crc: 12345678 --------------------------------- Done! pgqd/lib/mk/temos/index.txt0000664000401600040160000000251013175113172014226 0ustar cbecbe= Libusual/Antimake Build Demos = Here are few demos showing how to use both Libusual and Antimake in various scenarios. If you wonder why there is so many of them, the reason is that these demos are also used as regressions tests for various functionality. (Thus, 'temos') They are ordered in ascending complexity, so pick first one if you want simplest overview. == Reference Documentation == - link:antimake.html[AntiMake documentation] == Using Antimake == - link:antimake1.html[antimake1] - Project without subdirs - link:antimake2.html[antimake2] - Recursive subdirectories (SUBDIRS) - link:antimake3.html[antimake3] - Non-recursive subdirectories (EMBED_SUBDIRS) - link:antimake4.html[antimake4] - Using Autoconf. - link:antimake5.html[antimake5] - Shared libraries with Autoconf. - link:antimake6.html[antimake5] - Subdir stress test. == Using libusual == - link:libusual1.html[libusual1] - Local libusual: Linking against libusual.a with plain Make - link:libusual2.html[libusual2] - Local libusual: Embedding libusual modules with/witout Antimake - link:libusual3.html[libusual3] - Local libusual: Using top-level autoconf with Antimake - link:libusual4.html[libusual4] - Installed libusual: Plain make - link:libusual5.html[libusual5] - Installed libusual: Antimake - link:libusual6.html[libusual6] - Installed libusual: Autoconf pgqd/lib/mk/temos/libtemo.sh0000664000401600040160000000314713175113172014354 0ustar cbecbe LANG=C LC_ALL=C export LANG LC_ALL PATH=`pwd`/bin:$PATH export PATH set -e set -o pipefail SH="bash" unset MAKELEVEL MAKEFLAGS export MAKELEVEL MAKEFLAGS code=0 # we want to test local commits real_repo=../../.. # but final html should have fixed public url show_repo=git://github.com/libusual/libusual.git usual_clone() { enter_code echo "$ git clone $show_repo" "$@" git clone $real_repo "$@" } test_start() { rm -rf tmp mkdir tmp cd tmp } enter_code() { if test "$code" = "0"; then echo "---------------------------------" code=1 fi } leave_code() { if test "$code" = "1"; then echo "---------------------------------" code=0 fi } ls() { /bin/ls -C "$@" } title() { leave_code echo "" echo "=" "$@" "=" echo "" } title2() { leave_code echo "" echo "==" "$@" "==" echo "" } title3() { leave_code echo "" echo "===" "$@" "===" echo "" } run() { enter_code echo "$ $*" case "$1" in cd|ls|export) $* ;; *) $SH -c "$*" 2>&1 esac } runq() { enter_code echo "$ $*" echo "[...]" $SH -c "$*" > quiet.log 2>&1 || { tail -5 quiet.log; exit 1; } rm -f quiet.log } msg() { leave_code echo "" echo "$@" echo "" } longmsg() { leave_code echo "" sed 's/^ //' echo "" } cat_file() { leave_code mkdir -p `dirname $1` echo ".File: $1" case "$1" in *Makefile) echo "[source,makefile]" ;; *.[ch]) echo "[source,c]" ;; *.ac) echo "[source,autoconf]" ;; *.sh) echo "[source,shell]" ;; esac echo "-----------------------------------" sed 's/^ //' > $1 cat $1 echo "-----------------------------------" } pgqd/lib/mk/temos/src/0000775000401600040160000000000013175113172013147 5ustar cbecbepgqd/lib/mk/temos/src/libusual6.temo0000664000401600040160000000465213175113172015752 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Use installed libusual with autoconf and antimake. longmsg <<-MSG Install libusual and link against it. MSG title2 Build libusual usual_clone libusual run cd libusual runq ./autogen.sh runq './configure --disable-shared --prefix=`pwd`/../inst' runq make runq make install run cd .. title2 Build our own code msg Now we prepare our own code. msg First, this is the source file: cat_file prog.c <<"EOF" #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } EOF msg Autoconf setup cat_file autogen.sh <<"EOF" # use prepared autgen logic ../../std-autogen.sh ../../.. # fetch Antimake template from libusual cp ../../antimake.mk antimake.mk.in EOF cat_file extra.mk.in <<"EOF" USUAL_ANTIMAKE = @USUAL_ANTIMAKE@ USUAL_CFLAGS = @USUAL_CFLAGS@ USUAL_LIBS = @USUAL_LIBS@ EOF cat_file configure.ac <<"EOF" AC_INIT([achello], [0.1], [https://libusual.github.com]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_SRCDIR([prog.c]) AC_PREREQ([2.59]) AC_USUAL_INIT AC_USUAL_PROGRAM_CHECK PKG_CHECK_MODULES(USUAL, libusual) _PKG_CONFIG([USUAL_ANTIMAKE], [variable=antimake], [libusual]) AC_SUBST([USUAL_ANTIMAKE]) AC_OUTPUT([antimake.mk extra.mk]) EOF msg Here is the source that needs to be linked with libusual: cat_file prog.c <<"EOF" #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } EOF msg Antimake based Makefile cat_file Makefile <<"EOF" # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c prog_CPPFLAGS = $(USUAL_CFLAGS) prog_LDADD = $(USUAL_LIBS) # clean configured files DISTCLEANFILES = config.status config.log extra.mk antimake.mk config.h # clean generated files MAINTAINERCLEANFILES = configure config.guess config.sub install-sh \ antimake.mk.in extra.mk.in config.h.in EXTRA_DIST = Makefile $(MAINTAINERCLEANFILES) # launch Antimake include extra.mk include antimake.mk EOF msg Build the project run sh ./autogen.sh runq 'PKG_CONFIG_PATH=`pwd`/inst/lib/pkgconfig ./configure' run make run ls run ./prog run make am-show-distfiles run make maintainer-clean run ls msg Done! pgqd/lib/mk/temos/src/libusual2.temo0000664000401600040160000000313713175113172015743 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Embedding libusual as part of the source. longmsg <<-"MSG" Here we build libusual as part of top-level tree. This gives the advantage of building only the modules that are actually used in main tree and without the intermediate `libusual.a` step. This method is for projects that are developed in parallel with libusual. Not recommended for casual usage. MSG title2 Configure libusual msg Here we configure libusual, but do not build it. usual_clone lib run cd lib runq ./autogen.sh runq ./configure run cd .. title2 Prepare own code msg Here is the source that needs to be linked with libusual: cat_file prog.c <<"EOF" #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } EOF title2 Build with Antimake. longmsg <<-"MSG" Antimake is build framework on plain GNU Make that reads build instructons with Automake syntax. It has also hooks for libusual integration. Here is Makefile that uses Antimake: MSG cat_file Makefile <<"EOF" # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c # location of configured libusual USUAL_DIR = lib # mention that 'prog' wants embedded libusual prog_EMBED_LIBUSUAL = 1 # Load Antimake plugin that handles libusual embedding AM_FEATURES = libusual # launch Antimake include $(USUAL_DIR)/mk/antimake.mk EOF msg Build the project run make run ls run ./prog run make clean run ls msg Done pgqd/lib/mk/temos/src/libusual1.temo0000664000401600040160000000241713175113172015742 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Use libusual the simplest way longmsg <<-MSG Simplest usage would be to configure and build libusual locally and point your projects CPPFLAGS and LDFLAGS there. That way you get access to not only code but also various autoconfigued symbols without any complexities in your project. MSG title2 Build libusual usual_clone lib run cd lib runq ./autogen.sh runq ./configure --disable-shared --prefix=/opt runq make runq make install 'DESTDIR=`pwd`/../inst' run cd .. title2 Build our own code msg Now we prepare our own code. msg First, this is the source file: cat_file prog.c <<"EOF" #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } EOF msg Here is corresponding Makefile: cat_file Makefile <<"EOF" # here we describe our program SRCS = prog.c OBJS = $(SRCS:.c=.o) # here we link to libusual CPPFLAGS = -I./inst/opt/include LDFLAGS = -L./inst/opt/lib LIBS = -lusual CC = gcc CFLAGS = -O -g -Wall all: prog prog: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ EOF msg Build the project run make run ls run ./prog msg Done! pgqd/lib/mk/temos/src/antimake3.temo0000664000401600040160000000725613175113172015723 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Test Antimake EMBED_SUBDIRS longmsg <<-"MSG" Antimake variable `EMBED_SUBDIRS` list names of directories that contains Makefile fragmants that are to be embedded into current Makefile. - Plus: Proper dependencies, work well with parallel Make. - Minus: Cannot go into subdir and run make there. - Minus: Fragments are not stand-alone, so need some care when writing. MSG title2 Intro to EMBED_SUBDIRS longmsg <<-"MSG" To better understand what EMBED_SUBDIRS does, let\'s start with simple case - single Makefile that references files under subdir: MSG run mkdir -p src run cp ../../antimake.mk . cat_file Makefile <<"EOF" bin_PROGRAMS = src/myprog src_myprog_SOURCES = src/myprog.c include antimake.mk EOF cat_file src/myprog.c <<"EOF" #include int main(void) { printf("myprog\n"); return 0; } EOF run make run ./src/myprog run make clean longmsg <<"MSG" Now you can put the lines that reference files under `src/` also into `src` and include that from top-level Makefile: MSG cat_file src/Makefile.inc <<"EOF" bin_PROGRAMS = src/myprog src_myprog_SOURCES = src/myprog.c EOF cat_file Makefile <<"EOF" include src/Makefile.inc include antimake.mk EOF run make run ./src/myprog longmsg <<"MSG" This works but the problem is that although the Makefile is local, it still sees files from top-Makefile-level. So that is what `EMBED_SUBDIRS` helps with - it allow to use local filenames in Makefile fragment, and it converts them to top-level filenames when including. It knows only few type of variables it needs to convert: - target filenames in primares lists (*_PROGRAMS, *_LIBRARIES, etc) - target_SOURCES: foo_SOURCES -> sub_dir_foo_SOURCES with filename conversion - other target variables: `foo_*` -> `sub_dir_foo_*` without filename conversion - EXTRA_DIST, CLEANFILES, DISTCLEANFILES, MAINTAINERCLEANFILES Any other variables stay untouched, and obviously they can mess up top-level variables. So the included Makefile should be as clean as possible. MSG title2 Setup source tree for EMBED_SUBDIRS msg Setup directories, install Antimake run mkdir -p lib1/sublib lib2 run cp ../../antimake.mk . msg Prepare sources cat_file main.c <<"EOF" #include void func1(void); void func2(void); void func3(void); int main(void) { func1(); func2(); func3(); printf("main\n"); return 0; } EOF cat_file lib1/func1.c <<"EOF" #include void func1(void) { printf("func1\n"); } EOF cat_file lib1/sublib/func2.c <<"EOF" #include void func2(void) { printf("func2\n"); } EOF cat_file lib2/func3.c <<"EOF" #include void func3(void) { printf("func3\n"); } EOF msg Prepare Makefiles cat_file Makefile <<"EOF" PACKAGE_NAME = test-subdirs PACKAGE_VERSION = 1.0 EMBED_SUBDIRS = lib1 lib1/sublib lib2 bin_PROGRAMS = prog prog_SOURCES = main.c prog_LDADD = lib1/func1.a lib1/sublib/func2.a lib2/func3.a EXTRA_DIST = Makefile antimake.mk include antimake.mk EOF cat_file lib1/Makefile.am <<"EOF" noinst_LIBRARIES = func1.a func1_a_SOURCES = func1.c EXTRA_DIST = Makefile.am EOF cat_file lib1/sublib/Makefile.am <<"EOF" noinst_LIBRARIES = func2.a func2_a_SOURCES = func2.c EXTRA_DIST = Makefile.am EOF cat_file lib2/Makefile.am <<"EOF" noinst_LIBRARIES = func3.a func3_a_SOURCES = func3.c EXTRA_DIST = Makefile.am EOF title2 Building msg Build the project run make run ls run ./prog msg We can now install it: run make install prefix=/opt DESTDIR=./inst run ls ./inst/opt/bin msg Now we can create package that can be given to others. run make dist run ls run 'tar tzf test-subdirs-1.0.tar.gz | sort' msg Clean the tree run make clean run ls msg Done! pgqd/lib/mk/temos/src/libusual4.temo0000664000401600040160000000303513175113172015742 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Use installed libusual longmsg <<-MSG Install libusual and link against it. MSG title2 Build libusual usual_clone libusual run cd libusual runq ./autogen.sh runq './configure --disable-static --prefix=`pwd`/../inst' runq make runq make install run cd .. run 'find inst | sort' title2 Build our own code msg Now we prepare our own code. msg First, this is the source file: cat_file prog.c <<"EOF" #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } EOF msg Here is corresponding Makefile: cat_file Makefile <<"EOF" CC = gcc CFLAGS = -O -g -Wall # here we describe our program SRCS = prog.c OBJS = $(SRCS:.c=.o) # put libusual flags to proper place CPPFLAGS = $(USUAL_CPPFLAGS) LIBS = $(USUAL_LIBS) # use pkg-config to get libusual info USUAL_CPPFLAGS = $(shell $(PKG_CONFIG) --cflags libusual) USUAL_LIBS = $(shell $(PKG_CONFIG) --libs libusual) # temo hacks to support local install, not needed otherwise PKG_CONFIG := PKG_CONFIG_PATH=$(CURDIR)/inst/lib/pkgconfig pkg-config CPPFLAGS := $(subst $(CURDIR)/libusual/../,./,$(CPPFLAGS)) LIBS := $(subst $(CURDIR)/libusual/../,./,$(LIBS)) all: prog %.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< prog: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ EOF msg Build the project run make run ls run LD_LIBRARY_PATH=./inst/lib ./prog msg Done! pgqd/lib/mk/temos/src/antimake2.temo0000664000401600040160000000460413175113172015714 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Test Antimake SUBDIRS longmsg <<-"MSG" Antimake variable `SUBDIRS` list names of directories that the Make needs to recurse into. Each of them contains stand-alone Makefile that directs building in that subdirs. - Plus: you can call 'make' while being in subdir to build only local targets. - Minus: dependencies between subdirs do not work. MSG title2 Setup source tree msg Setup directories, install Antimake run mkdir -p lib1/sublib lib2 run cp ../../antimake.mk . msg Prepare sources cat_file api.h <<"EOF" void func1(void); void func2(void); void func3(void); EOF cat_file main.c <<"EOF" #include #include "api.h" int main(void) { func1(); func2(); func3(); printf("main\n"); return 0; } EOF cat_file lib1/func1.c <<"EOF" #include #include "api.h" void func1(void) { printf("func1\n"); } EOF cat_file lib1/sublib/func2.c <<"EOF" #include #include "api.h" void func2(void) { printf("func2\n"); } EOF cat_file lib2/func3.c <<"EOF" #include #include "api.h" void func3(void) { printf("func3\n"); } EOF msg Prepare Makefiles cat_file Makefile <<"EOF" PACKAGE_NAME = test-subdirs PACKAGE_VERSION = 1.0 SUBDIRS = lib1 lib2 bin_PROGRAMS = prog prog_SOURCES = main.c prog_LDADD = lib1/func1.a lib1/sublib/func2.a lib2/func3.a EXTRA_DIST = Makefile antimake.mk include antimake.mk EOF cat_file lib1/Makefile <<"EOF" SUBLOC = lib1 SUBDIRS = sublib noinst_LIBRARIES = func1.a func1_a_SOURCES = func1.c func1_a_CPPFLAGS = -I.. EXTRA_DIST = Makefile include ../antimake.mk EOF cat_file lib1/sublib/Makefile <<"EOF" SUBLOC = lib1/sublib noinst_LIBRARIES = func2.a func2_a_SOURCES = func2.c func2_a_CPPFLAGS = -I../.. EXTRA_DIST = Makefile include ../../antimake.mk EOF cat_file lib2/Makefile <<"EOF" SUBLOC = lib2 noinst_LIBRARIES = func3.a func3_a_SOURCES = func3.c func3_a_CPPFLAGS = -I$(top_srcdir) EXTRA_DIST = Makefile include ../antimake.mk EOF title2 Building msg Build the project run make run ls run ./prog msg We can now install it: run make install DESTDIR=./inst run ls ./inst/usr/local/bin msg Now we can create package that can be given to others. run make dist run ls run 'tar tzf test-subdirs-1.0.tar.gz | sort' msg Clean the tree run make clean run ls msg Test O= run mkdir -p build run make O=build run ls run ls build msg Done! pgqd/lib/mk/temos/src/libusual5.temo0000664000401600040160000000263113175113172015744 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Use installed libusual with antimake. longmsg <<-MSG Install libusual and link against it. MSG title2 Build libusual usual_clone libusual run cd libusual runq ./autogen.sh runq './configure --disable-shared --prefix=`pwd`/../inst' runq make runq make install run cd .. title2 Build our own code msg Now we prepare our own code. msg First, this is the source file: cat_file prog.c <<"EOF" #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } EOF msg Here is corresponding Makefile: cat_file Makefile <<"EOF" # temo hacks to support local install, not needed otherwise PKG_CONFIG = PKG_CONFIG_PATH=$(CURDIR)/inst/lib/pkgconfig pkg-config # use pkg-config to get libusual info USUAL_CPPFLAGS = $(shell $(PKG_CONFIG) libusual --cflags) USUAL_LDFLAGS = $(shell $(PKG_CONFIG) libusual --libs-only-L) USUAL_LIBS = $(shell $(PKG_CONFIG) libusual --libs-only-l) # Generic Antimake bin_PROGRAMS = prog prog_SOURCES = prog.c prog_CPPFLAGS = $(USUAL_CPPFLAGS) prog_LDFLAGS = $(USUAL_LDFLAGS) prog_LDADD = $(USUAL_LIBS) # use installed Antimake ANTIMAKE = $(shell $(PKG_CONFIG) libusual --variable=antimake) include $(ANTIMAKE) EOF msg Build the project run make run ls run ./prog msg Done! pgqd/lib/mk/temos/src/antimake1.temo0000664000401600040160000000267313175113172015717 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Test Antimake title2 Simplest usage msg Here we avoid use of any autotools. msg First, this is the source file: cat_file hello.c <<"EOF" #include int main(void) { printf("Hello, world\n"); return 0; } EOF msg Here is corresponding Makefile: cat_file Makefile <<"EOF" # This is target list - it's name describes target type # and how it is installed, it's value target files to be built. # bin - the targets will be installed under $(bindir) # PROGRAMS - the target is executable built from many sources bin_PROGRAMS = hello # The target 'hello'-s source file list. hello_SOURCES = hello.c # Run Antimake include antimake.mk EOF msg Also install Antimake and we are ready to build: run cp ../../antimake.mk . run ls msg Build the project run make run ls run ./hello msg We can even install it already: run make install prefix=/opt DESTDIR=./inst run ls ./inst/opt/bin msg For creating source package, we need to provide additional info: cat_file Makefile <<"EOF" # Package name and version for tarball filename PACKAGE_NAME = myhello PACKAGE_VERSION = 1.0 # Non-source files to put into tarball EXTRA_DIST = Makefile antimake.mk bin_PROGRAMS = hello hello_SOURCES = hello.c include antimake.mk EOF msg Now we can create package that can be given to others. run make dist run ls run 'tar tzf myhello-1.0.tar.gz | sort' msg Clean the tree run make clean run ls msg Done! pgqd/lib/mk/temos/src/antimake5.temo0000664000401600040160000000344713175113172015723 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Shared libraries and autoconf msg Autoconf setup cat_file autogen.sh <<"EOF" ../../std-autogen.sh ../../.. # fetch Antimake template from libusual cp ../../antimake.mk antimake.mk.in EOF cat_file configure.ac <<"EOF" AC_INIT([actest], [0.1]) AC_CONFIG_SRCDIR([prog.c]) AC_PREREQ([2.59]) LT_INIT AC_USUAL_INIT AC_USUAL_PROGRAM_CHECK AC_OUTPUT([antimake.mk]) EOF msg Here are the source files: cat_file prog.c <<"EOF" void func1(void); int main(void) { func1(); return 0; } EOF cat_file func.c <<"EOF" #include void func1(void); void func1(void) { printf("hello from func1\n"); } EOF msg Antimake based Makefile cat_file Makefile <<"EOF" lib_LTLIBRARIES = libtemo.la libtemo_la_SOURCES = func.c libtemo_la_LDFLAGS = -version-info 3:0:2 bin_PROGRAMS = prog prog_SOURCES = prog.c prog_LDADD = libtemo.la # clean configured files DISTCLEANFILES = \ config.status \ config.log \ antimake.mk \ libtool # clean generated files MAINTAINERCLEANFILES = \ configure \ config.guess \ config.sub \ install-sh \ antimake.mk.in \ ltmain.sh EXTRA_DIST = \ Makefile \ $(MAINTAINERCLEANFILES) # launch Antimake include antimake.mk EOF msg Build the project run sh ./autogen.sh runq ./configure run make run ls run ./prog msg Create distribution package run make dist run 'tar tzf actest-0.1.tar.gz | sort' msg Test installation run 'make install DESTDIR=/tmp/test-inst' run ls run 'find /tmp/test-inst | sort' run rm -rf /tmp/test-inst msg Test the distribution package and separate build dir run mkdir -p test run cd test run tar xf ../actest-0.1.tar.gz run mkdir build run cd build runq ../actest-0.1/configure run make run ls run ./prog run cd ../.. msg Clean up run make maintainer-clean run ls msg Done pgqd/lib/mk/temos/src/antimake4.temo0000664000401600040160000000267413175113172015723 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Using Antimake with autoconf msg Autoconf setup cat_file autogen.sh <<"EOF" ../../std-autogen.sh ../../.. # fetch Antimake template from libusual cp ../../antimake.mk antimake.mk.in EOF cat_file configure.ac <<"EOF" AC_INIT([actest], [0.1]) AC_CONFIG_SRCDIR([prog.c]) AC_PREREQ([2.59]) AC_USUAL_INIT AC_USUAL_PROGRAM_CHECK AC_OUTPUT([antimake.mk]) EOF msg Here is the source we want to build: cat_file prog.c <<"EOF" #include #include int main(void) { printf("hello\n"); return 0; } EOF msg Antimake based Makefile cat_file Makefile <<"EOF" # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c EXTRA_DIST = Makefile $(MAINTAINERCLEANFILES) # clean configured files DISTCLEANFILES = config.status config.log antimake.mk # clean generated files MAINTAINERCLEANFILES = configure config.guess config.sub install-sh antimake.mk.in # launch Antimake include antimake.mk EOF msg Build the project run sh ./autogen.sh runq ./configure run make run ls run ./prog msg Create distribution package run make dist run 'tar tzf actest-0.1.tar.gz | sort' msg Test the distribution package and separate build dir run mkdir -p test run cd test run tar xf ../actest-0.1.tar.gz run mkdir build run cd build runq ../actest-0.1/configure run make run ls run ./prog run cd ../.. msg Clean up run make maintainer-clean run ls msg Done pgqd/lib/mk/temos/src/libusual3.temo0000664000401600040160000000342113175113172015740 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Using Autoconf and embedded libusual longmsg <<-"MSG" MSG title2 Fetch libusual msg Here we close libusual repo, but do not configure nor build it. usual_clone lib msg Autoconf setup cat_file autogen.sh <<"EOF" # use prepared autgen logic ./lib/mk/std-autogen.sh ./lib # fetch Antimake template from libusual cp lib/mk/antimake.mk antimake.mk.in EOF cat_file configure.ac <<"EOF" AC_INIT([achello], [0.1], [https://libusual.github.com]) AC_CONFIG_SRCDIR([prog.c]) AC_CONFIG_HEADER([lib/usual/config.h]) AC_PREREQ([2.59]) AC_USUAL_PORT_CHECK AC_USUAL_PROGRAM_CHECK AC_USUAL_HEADER_CHECK AC_USUAL_TYPE_CHECK AC_USUAL_FUNCTION_CHECK AC_OUTPUT([antimake.mk]) EOF msg Here is the source that needs to be linked with libusual: cat_file prog.c <<"EOF" #include #include #include int main(void) { const char *data = "CECSFXX"; uint32_t crc; crc = calc_crc32(data, strlen(data), 0); printf("crc: %08x\n", crc); return 0; } EOF msg Antimake based Makefile cat_file Makefile <<"EOF" # the automake-style build description for 'prog' noinst_PROGRAMS = prog prog_SOURCES = prog.c # location of configured libusual USUAL_DIR = lib # mention that 'prog' wants embedded libusual prog_EMBED_LIBUSUAL = 1 AM_FEATURES = libusual # clean configured files DISTCLEANFILES = config.status config.log \ antimake.mk $(USUAL_DIR)/usual/config.h # clean generated files MAINTAINERCLEANFILES = configure config.guess config.sub install-sh \ antimake.mk.in $(USUAL_DIR)/usual/config.h.in # launch Antimake include $(USUAL_DIR)/mk/antimake.mk EOF msg Build the project run sh ./autogen.sh runq ./configure run make run ls run ./prog run make maintainer-clean run ls msg Done pgqd/lib/mk/temos/src/antimake6.temo0000664000401600040160000001043113175113172015713 0ustar cbecbe . ./libtemo.sh || exit 1 test_start title Antimake stress-test msg Autoconf setup cat_file autogen.sh <<"EOF" ../../std-autogen.sh ../../.. # fetch Antimake template from libusual make -f ../../antimake.mk show-config > build.mk.in echo 'include $(abs_top_srcdir)/antimake.mk' >> build.mk.in ln -sf ../../antimake.mk . ln -sf ../../amext-cxx.mk . EOF cat_file configure.ac <<"EOF" AC_INIT([actest], [0.1]) AC_CONFIG_SRCDIR([esub/prog.c]) AC_PREREQ([2.59]) AC_USUAL_INIT LT_INIT AC_USUAL_PROGRAM_CHECK AC_PROG_CXX AC_OUTPUT([build.mk]) EOF msg Here are the source files: longmsg <<"EOF" ------------- ./prog.c ./cpptest.cpp ./sub/func1.c ./sub/esub/func2,c ./sub/sub/func3.c ./esub/func4.c ./esub/esub/func5.c ./esub/sub/func6.c ------------- EOF run mkdir -p sub/esub sub/sub esub/esub esub/sub cat_file esub/prog.c <<"EOF" #include "func1.h" #include "func2.h" #include "func3.h" #include "func4.h" #include "func5.h" #include "func6.h" #include int main(void) { printf("%s\n", __FILE__); func1(); func2(); func3(); func4(); func5(); func6(); return 0; } EOF cat_file cpptest.cpp <<"EOF" #include using namespace std; int main(void) { cout << "Hello" << endl; return 0; } EOF cat_file sub/func1.c <<"EOF" #include #include "func1.h" void func1(void) { printf("%s\n", __FILE__); } EOF cat_file sub/func1.h <<"EOF" void func1(void); EOF run 'sed s/1/2/ sub/func1.c > sub/esub/func2.c' run 'sed s/1/2/ sub/func1.h > sub/esub/func2.h' run 'sed s/1/3/ sub/func1.c > sub/sub/func3.c' run 'sed s/1/3/ sub/func1.h > sub/sub/func3.h' run 'sed s/1/4/ sub/func1.c > esub/func4.c' run 'sed s/1/4/ sub/func1.h > esub/func4.h' run 'sed s/1/5/ sub/func1.c > esub/sub/func5.c' run 'sed s/1/5/ sub/func1.h > esub/sub/func5.h' run 'sed s/1/6/ sub/func1.c > esub/esub/func6.c' run 'sed s/1/6/ sub/func1.h > esub/esub/func6.h' msg Now fill makefiles cat_file Makefile <<"EOF" SUBDIRS = sub EMBED_SUBDIRS = esub AM_FEATURES = cxx override WFLAGS = -Wall EXTRA_DIST = Makefile antimake.mk amext-cxx.mk $(MAINTAINERCLEANFILES) # clean configured files DISTCLEANFILES = \ config.status \ config.log \ libtool # clean generated files MAINTAINERCLEANFILES = \ configure \ config.guess \ config.sub \ install-sh \ build.mk.in \ ltmain.sh noinst_PROGRAMS = cpptest cpptest_SOURCES = cpptest.cpp # launch Antimake include build.mk EOF cat_file sub/Makefile <<"EOF" SUBLOC = sub SUBDIRS = sub EMBED_SUBDIRS = esub noinst_LIBRARIES = libfunc1.a libfunc1_a_SOURCES = func1.c func1.h EXTRA_DIST = Makefile include ../build.mk EOF cat_file sub/sub/Makefile <<"EOF" SUBLOC = sub/sub EXTRA_DIST = Makefile noinst_LIBRARIES = libfunc3.a libfunc3_a_SOURCES = func3.c func3.h include ../../build.mk EOF cat_file sub/esub/Makefile.am <<"EOF" noinst_LIBRARIES = libfunc2.a libfunc2_a_SOURCES = func2.c func2.h EXTRA_DIST = Makefile.am EOF cat_file esub/Makefile.am <<"EOF" SUBDIRS = sub EMBED_SUBDIRS = esub EXTRA_DIST = Makefile.am noinst_LIBRARIES = libfunc4.a libfunc4_a_SOURCES = func4.c func4.h noinst_PROGRAMS = prog prog_SOURCES = prog.c prog_LDFLAGS = -L../sub -L../sub/esub -L. -Lsub prog_LDADD = \ -lfunc1 \ -lfunc2 \ $(topdir)/sub/sub/libfunc3.a \ -lfunc4 \ -lfunc5 \ esub/libfunc6.a prog_CFLAGS = -I../sub prog_CPPFLAGS = \ -I../sub/esub \ -I$(topdir)/sub/sub \ -I. \ -Iesub \ -I./sub EOF cat_file esub/sub/Makefile <<"EOF" SUBLOC = esub/sub EXTRA_DIST = Makefile noinst_LIBRARIES = libfunc5.a libfunc5_a_SOURCES = func5.c func5.h include ../../build.mk EOF cat_file esub/esub/Makefile.am <<"EOF" EXTRA_DIST = Makefile.am noinst_LIBRARIES = libfunc6.a libfunc6_a_SOURCES = func6.c func6.h EOF msg Build the project run sh ./autogen.sh runq ./configure #run make esub/prog run make run ls run ./esub/prog msg Create distribution package run make dist run 'tar tzf actest-0.1.tar.gz | sort' msg Test installation run 'make install DESTDIR=`pwd`/inst' run ls run 'find inst | sort' msg Test the distribution package and separate build dir run mkdir -p test run cd test run tar xf ../actest-0.1.tar.gz run mkdir build run cd build runq ../actest-0.1/configure run make run ls run make esub/prog run ./esub/prog run cd ../.. msg Clean up run make maintainer-clean run ls msg Done pgqd/lib/mk/temos/Makefile0000664000401600040160000000276313175113172014030 0ustar cbecbe# # This Makefile does not use Antimake because it must work # even when Antimake is broken. # TEMOS = \ antimake1 antimake2 antimake3 antimake4 antimake5 antimake6 \ libusual1 libusual2 libusual3 libusual4 libusual5 libusual6 \ OUT = $(addsuffix .txt,$(addprefix output/, $(TEMOS))) HTML = $(addsuffix .html,$(addprefix html/, $(TEMOS))) \ html/index.html html/antimake.html ExpFile = $(subst .temo,.txt,$(subst src/,expected/,$(1))) OutFile = $(subst .temo,.txt,$(subst src/,output/,$(1))) V ?= 0 ifeq ($(V),0) E = @echo Q = @ else E = @true Q = endif all: qtest .PHONY: all test ack clean html html: $(HTML) am: rm -f html/anti* make ftest: clean qtest qtest: $(OUT) @diff -urN expected output > regressions.diff || { \ less regressions.diff; \ echo "FAIL: Result does not match expected output"; \ exit 1; \ } $(Q) rm -f regressions.diff $(E) "All OK" #../antimake.mk ../../m4/usual.m4 output/%.txt: src/%.temo libtemo.sh @mkdir -p output @printf "$< ... " @bash $< > $@ 2>&1 && { cmp -s $@ $(call ExpFile,$<) && echo ok || echo failed; } \ || { echo "$< failed:" ; tail $(call OutFile,$<); exit 1; } html/%.html: output/%.txt @mkdir -p html asciidoc -o - $< | grep -v '^Last updated ' > $@ ack: cp output/*.txt expected/ clean: rm -rf tmp html output regressions.diff html/index.html: index.txt @mkdir -p html asciidoc -o - $< | grep -v '^Last updated ' > $@ html/antimake.html: ../antimake.txt @mkdir -p html asciidoc -a toc -o - $< | grep -v '^Last updated ' > $@ pgqd/lib/mk/amext-modes.mk0000664000401600040160000000446213175113172014013 0ustar cbecbe# # Custom compilation modes # Compile one target several times with different # configuration variables. # # Sample: # CFLAGS = -O2 # bin_PROGRAM = prog # prog_SOURCES = prog.c # # AM_MODES = debug # CFLAGS_debug = -O0 -g # # Result: # prog - compiled with -O2 # prog-debug - compiled with -O0 -g # AM_MODES ?= # Variables that can be overrided with $(var)_$(mode) AM_MODE_OVERRIDE += CC CXX CFLAGS CPPFLAGS DEFS LDFLAGS LIBS ## add "-MODE" string before file extension # 1-mode, 2-filename ModeName = $(basename $(2))-$(1)$(suffix $(2)) ## add mode suffix to all plain filenames # 1-mode, 2-file names, options ModeFilter = $(foreach f,$(2),$(if $(filter /% -%,$(f)),$(f),$(call ModeName,$(1),$(f)))) ## set per-target var # 1-dbgvar, 2-var, 3-final ModeVarX = $(3): $(2) = $$($(1))$(NewLine) # 1-mode, 2-var, 3-final ModeVarOverride = $(if $($(2)_$(1)),$(call ModeVarX,$(2)_$(1),$(2),$(3))) # 1-mode, 2-final ModeVarOverrideAll = $(foreach v,$(AM_MODE_OVERRIDE),$(call ModeVarOverride,$(1),$(v),$(2))) ## copy target, replace vars # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags,6=mode,7-newtgt,8-cleantgt,9-list define AddModes4 $(trace8) $(IFEQ) ($$(filter $(9),$$(am_TARGETLISTS)),) am_TARGETLISTS += $(9) $(ENDIF) # add new target to old list $(9) += $(7) # copy details, change library names $(8)_SOURCES := $$($(1)_SOURCES) nodist_$$(8)_SOURCES := $$(nodist_$(1)_SOURCES) $(8)_CPPFLAGS := $$($(1)_CPPFLAGS) $(8)_CFLAGS := $$($(1)_CFLAGS) $(8)_LDFLAGS := $$($(1)_LDFLAGS) $(8)_LIBADD := $$(call ModeFilter,$(6),$$($(1)_LIBADD)) $(8)_LDADD := $$(call ModeFilter,$(6),$$($(1)_LDADD)) # add variable replacements $(call ModeVarOverrideAll,$(6),$(call FinalTargetFile,$(8),$(7),$(3))) endef ## add clean name, list name # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags,6-mode,7-raw tgt AddModes3 = $(call AddModes4,$(1),$(2),$(3),$(4),$(5),$(6),$(7),$(call CleanName,$(7)),$(subst $(Space),_,$(5)_$(4)_$(3))) ## loop over modes # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags AddModes2 = $(trace5)$(foreach m,$(AM_MODES),$(call AddModes3,$(1),$(2),$(3),$(4),$(5),$(m),$(call ModeName,$(m),$(2)))) ## ignore small primaries # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags AddModes = $(trace5)$(if $(filter $(3),$(AM_BIG_PRIMARIES)),$(call AddModes2,$(1),$(2),$(3),$(4),$(5))) # Install hook AM_TARGET_HOOKS += AddModes pgqd/lib/mk/amext-libusual.mk0000664000401600040160000000325513175113172014523 0ustar cbecbe# # Merge libusual sources with target sources # # Usage: # USUAL_DIR = # # _EMBED_LIBUSUAL = 1 # # It adds module sources into _SOURCES # and -I$(USUAL_DIR) to _CPPFLAGS. # ## ## Utility functions for libusual link ## _USUAL_DIR = $(call JoinPath,$(srcdir),$(USUAL_DIR)) _USUAL_DIR2 = $(call JoinPath,$(_USUAL_DIR),usual) # module names from sources (plus headers) UsualMods = $(trace1)$(shell $(_USUAL_DIR)/find_modules.sh $(_USUAL_DIR) $(sort $(wildcard $(addprefix $(srcdir)/,$(1))))) # full-path sources based on module list UsualSrcsFull = $(trace1)$(sort $(wildcard $(addprefix $(_USUAL_DIR2)/,$(addsuffix *.[ch],$(1))))) # remove USUAL_DIR UsualStrip = $(trace1)$(subst $(_USUAL_DIR)/,,$(1)) # simple-path sources based on module list UsualSrcs = $(call UsualStrip,$(call UsualSrcsFull,$(1))) # usual sources from user source file list UsualSources = $(if $(1),$(call UsualSrcsFull,$(call UsualMods,$(1)))) # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define EmbedLibUsual $(trace5) # embed libusual objects directly $(IFEQ) ($$($(1)_EMBED_LIBUSUAL),1) $(1)_SOURCES := $$($(1)_SOURCES) $$(call UsualSources, $$($(1)_SOURCES)) EXTRA_$(1)_SOURCES := $$(EXTRA_$(1)_SOURCES) \ $$(call UsualSources, \ $$(EXTRA_$(1)_SOURCES) \ $$(nodist_$(1)_SOURCES) \ $$(nodist_EXTRA_$(1)_SOURCES)) $(1)_CPPFLAGS += -I$$(USUAL_DIR) # add libusual to vpath $(IFEQ) ($$(filter $$(USUAL_DIR),$$(VPATH)),) VPATH += $$(USUAL_DIR) $(IFNEQ) ($$(srcdir),$$(builddir),) VPATH += $$(call JoinPath,$$(srcdir),$$(USUAL_DIR)) $(ENDIF) $(ENDIF) $(ENDIF) endef AM_TARGET_HOOKS += EmbedLibUsual EXTRA_DIST += $(_USUAL_DIR)/find_modules.sh $(_USUAL_DIR)/usual/config.h.in pgqd/lib/mk/safe-headers.sed0000664000401600040160000000020013175113172014245 0ustar cbecbes/HAVE_/USUAL_&/g s/PACKAGE_/USUAL_&/g s/LT_OBJ/USUAL_&/g s/STRERROR_/USUAL_&/g s/CASSERT/USUAL_&/g s/WORDS_BIGENDIAN/USUAL_&/g pgqd/lib/mk/amext-cxx.mk0000664000401600040160000000167013175113172013504 0ustar cbecbe # # Support for C++ language # # - extensions: .cc, .cpp, cxx # - CXX, CXXFLAGS # - AM_CXXFLAGS, _CXXFLAGS # # autoconfigurable values ifneq ($(filter-out @%,@CXX@),) CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ endif CXX ?= c++ CXXFLAGS ?= -O -g # fixme: add warning flags to CXXFLAGS CXXFLAGS += $(WFLAGS) # helper variables CXXLD ?= $(CXX) CXXCOMPILE ?= $(CXX) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXLINK ?= $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ # full compile command define AM_LANG_CXX_COMPILE $(E) "CXX" $< $(Q) $(LTCOMPILE) $(CXXCOMPILE) $(OBJDEPS) -c -o $@ $< endef # full link command define AM_LANG_CXX_LINK $(E) "CXXLD" $@ $(Q) $(LTLINK) $(CXXLINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) endef # source file extensions for c++ AM_LANG_CXX_SRCEXTS = .cc .cpp cxx # register per-target variable AM_TARGET_VARIABLES += CXXFLAGS # register new language AM_LANGUAGES += CXX pgqd/lib/mk/antimake.mk0000775000401600040160000012200013175113172013351 0ustar cbecbe#! /usr/bin/make -f # # antimake.mk - automake syntax with GNU Make # # Copyright (c) 2011 Marko Kreen # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Goals: # - Clean user Makefiles, by using automake syntax # - Clean output during build # - Optional ties with `autoconf` and `libtool` # - Automatic dependency tracking # - Avoid separate build step for Makefiles # - No extra tools needed except GNU Make # Usage without autoconf: # - copy antimake.mk into source dir, then: include antimake.mk # - copy/link antimake.mk into PATH, then: include $(shell antimake.mk) # # Usage with autoconf: # - Copy to antimake.mk.in at top dir, then process with autoconf # to antimake.mk and include that one in Makefiles. # # - Have config.mak.in that also includes antimake.mk. # Suggestion: the separate file should include antimake.mk # using $(abs_top_srcdir) to support separate build dir. # # - Include config and antimake.mk separately in user Makefiles ## ## Startup hacks ## # detect GNU make version, confuse others $(eval GNUMAKE380=1) GNUMAKE381=$(or ,$(GNUMAKE380)) define GNUMAKE382 = $(GNUMAKE381) endef # give error of too old ifeq ($(GNUMAKE381),) $(error GNU Make 3.81+ required) endif # extra targets if this file is executed directly ifeq ($(words $(MAKEFILE_LIST)), 1) .PHONY: show-location show-config # default: print location. For "include $(shell antimake.mk)"-style usage. show-location: @echo $(MAKEFILE_LIST) # show autoconfigurable variables show-config: @grep '@[^ ]*@$$' $(MAKEFILE_LIST) endif ## ## Allow this file to be processed through autoconf ## # # to extract autoconfigurable values: # $ grep '@[^ ]*@$' antimake.mk > config.mk.in # $ antimake.mk show-config > config.mk.in # ifneq ($(filter-out @%,@PACKAGE_NAME@),) PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PORTNAME = @PORTNAME@ EXEEXT = @EXEEXT@ HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ # C language CC = @CC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ WFLAGS = @WFLAGS@ # linking LD = @LD@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ # static and shared libs AR = @AR@ ARFLAGS = @ARFLAGS@ RANLIB = @RANLIB@ LIBTOOL = @LIBTOOL@ # other tools SHELL = @SHELL@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_DATA = @INSTALL_DATA@ MKDIR_P = @MKDIR_P@ SED = @SED@ AWK = @AWK@ GREP = @GREP@ EGREP = @EGREP@ STRIP = @STRIP@ # install locations prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datarootdir = @datarootdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ docdir = @docdir@ mandir = @mandir@ libdir = @libdir@ localedir = @localedir@ pkgdatadir = @pkgdatadir@ pkgconfigdir = @pkgconfigdir@ aclocaldir = @aclocaldir@ # autoconf values for top dir abs_top_srcdir ?= @abs_top_srcdir@ abs_top_builddir ?= @abs_top_builddir@ nosub_top_srcdir ?= @top_srcdir@ nosub_top_builddir ?= @top_builddir@ endif # end of @xx@ values ## ## In case of missing autoconf values, provide sane defaults ## PACKAGE_NAME ?= package PACKAGE_TARNAME ?= $(PACKAGE_NAME) PACKAGE_VERSION ?= 0.0 PACKAGE_STRING ?= $(PACKAGE_NAME) $(PACKAGE_VERSION) PACKAGE_URL ?= PACKAGE_BUGREPORT ?= PORTNAME ?= unix EXEEXT ?= HAVE_CC_DEPFLAG ?= yes # C language CC ?= cc CPP ?= cpp CPPFLAGS ?= CFLAGS ?= -O -g DEFS ?= # warning flags are keps separately to allow easy override WFLAGS ?= -Wall # add them to main flags now CFLAGS += $(WFLAGS) # linking LD ?= ld LDFLAGS ?= LIBS ?= # static and shared libs LIBTOOL ?= libtool AR ?= ar ARFLAGS ?= rcs ifeq ($(ARFLAGS),rv) ARFLAGS = rcs endif RANLIB ?= ranlib # other tools SHELL ?= /bin/sh INSTALL ?= install INSTALL_PROGRAM ?= $(INSTALL) INSTALL_SCRIPT ?= $(INSTALL) INSTALL_DATA ?= $(INSTALL) MKDIR_P ?= mkdir -p SED ?= sed AWK ?= awk GREP ?= grep EGREP ?= grep -E STRIP ?= strip # install locations prefix ?= /usr/local exec_prefix ?= ${prefix} bindir ?= ${exec_prefix}/bin includedir ?= ${prefix}/include sbindir ?= ${exec_prefix}/sbin libexecdir ?= ${exec_prefix}/libexec datarootdir ?= ${prefix}/share datadir ?= ${datarootdir} sysconfdir ?= ${prefix}/etc docdir ?= ${datarootdir}/doc/${PACKAGE_TARNAME} mandir ?= ${datarootdir}/man libdir ?= ${exec_prefix}/lib localedir ?= ${datarootdir}/locale pkgdatadir ?= ${datarootdir}/${PACKAGE_TARNAME} pkgconfigdir ?= ${libdir}/pkgconfig aclocaldir ?= ${datarootdir}/aclocal # autoconf values for top dir abs_top_srcdir ?= $(CURDIR) abs_top_builddir ?= $(CURDIR) # make sure nosub vals are not empty ifeq ($(nosub_top_builddir),) nosub_top_builddir = . endif ifeq ($(nosub_top_srcdir),) nosub_top_srcdir = . endif ## ## Variables for user makefiles ## # current subdirectory location from top dir (foo/bar) SUBLOC ?= . # subdirectories in current directory SUBDIRS ?= # extra files for clean targets CLEANFILES ?= DISTCLEANFILES ?= MAINTAINERCLEANFILES ?= # Additional flags for Makefile use, to avoid need # to touch flags coming from autoconf/cmdline AM_DEFS ?= AM_CPPFLAGS ?= AM_CFLAGS ?= AM_LDFLAGS ?= AM_LIBTOOLFLAGS ?= AM_MAKEFLAGS ?= AM_LIBS ?= # libusual sources, for embedded usage USUAL_DIR ?= . # V=1 -> verbose build V ?= 0 # turn on function tracing AM_TRACE ?= # default formats for 'dist' AM_DIST_DEFAULT ?= gzip ## ## Non-user-serviceable area ## # Hacking: # # - Uppercase names are simple (late) variables, lowercase names - targets, # mixedcase - functions that need to be $(call)-ed. # # - Minimal amount of shell should be used here. # # - Minimal amount of := and $(eval) # # - It's useful to indent the expressions for easier understanding. # Later the indendation needs to be removed, as whitespace is significant for Make. # Several functions must not add any extra whitespace. # # GNU Make features in new versions: # # 3.80 - 2002-10-03: base version. $(eval) $(value) $(MAKEFILE_LIST) $(.VARIABLES) $(call fixes) # 3.81 - 2006-04-01: $(or), $(and), $(lastword), $(abspath), $(realpath), $(info), $(flavor) # 3.82 - 2010-07-28: private, undefine, define var := # # This file should use only features from 3.80 ## ## command helpers ## CCLD ?= $(CC) COMPILE ?= $(CC) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LINK ?= $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_AR ?= $(AR) $(ARFLAGS) LIBTOOLCMD ?= $(LIBTOOL) $(LIBTOOLQ) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) RM = rm -f ## ## Internals ## # varables that can be set per-target with target_VAR # they appear as AM_foo. [Not supported: COMPILE] AM_TARGET_VARIABLES += CFLAGS CPPFLAGS LDFLAGS LIBTOOLFLAGS DEFS LIBS # list of language (rather compiler) names AM_LANGUAGES += C AM_BIG_PRIMARIES += LIBRARIES LTLIBRARIES PROGRAMS AM_SMALL_PRIMARIES += HEADERS SCRIPTS DATA MANS # list of destinations per primary AM_DESTINATIONS += bin lib libexec sbin \ data doc include locale man sysconf \ pkgdata pkgconfig aclocal \ noinst EXTRA # primaries where 'dist' is default AM_DIST_PRIMARIES += HEADERS AM_PRIMARIES = $(AM_BIG_PRIMARIES) $(AM_SMALL_PRIMARIES) # distclean does rm -rf on that OBJDIR = .objs # extension for objects OBJEXT = .o # extension for static libraries LIBEXT = .a # files that need to be converted to objects AM_SRCEXTS = $(foreach lang,$(AM_LANGUAGES),$(AM_LANG_$(lang)_SRCEXTS)) # target types - big/small: with/without objects # list of flags, 'noinst' is taken as dest, 'base' is always default AM_FLAGS = base nobase dist nodist ## configure non-defult target params AM_PROGRAMS_InstFunc = ProgInstall AM_LTLIBRARIES_InstFunc = LTLibInstall AM_LTLIBRARIES_OBJEXT = .lo AM_SCRIPTS_InstFunc = ScriptInstall AM_MANS_InstFunc = ManInstall # files to distribute am_DISTFILES := am_FINAL_DISTFILES = $(sort $(am_DISTFILES)) AM_DIST_BASE = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) AM_ALL_TARGETS = ## ## Make dependencies work ## HAVE_CC_DEPFLAG ?= yes ifeq ($(HAVE_CC_DEPFLAG),yes) OBJDEPS = -MD -MP -MT $@ -MF $@.d endif ## ## Quiet by default, 'make V=1' shows commands ## # 1-dir MkDir = $(MKDIR_P) $(1) # 1-fmt, 2-args Printf = printf $(1) $(2) CTX ?= ifeq ($(V), 0) E = @$(call Printf,"%-4s %-8s %s\n","$(CTX)") Q = @ LIBTOOLQ = --silent MAKEFLAGS += --no-print-directory else E = @true Q = LIBTOOLQ = --silent endif ## ## libtool activation ## # libtool activates when detects %.lo / %.la pattern LTCOMPILE = $(if $(filter %.lo,$@),$(LIBTOOLCMD) --mode=compile) LTLINK = $(if $(filter %.la %.lo,$^),$(LIBTOOLCMD) --mode=link) LTCLEAN = $(LIBTOOLCMD) --mode=clean ## ## Default setup for C ## AM_LANG_C_SRCEXTS = .c define AM_LANG_C_COMPILE $(E) "CC" $< $(Q) $(LTCOMPILE) $(COMPILE) $(OBJDEPS) -c -o $@ $< endef define AM_LANG_C_LINK $(E) "CCLD" $@ $(Q) $(LTLINK) $(LINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) endef ## ## Various other shortcuts ## define ar_lib $(E) "AR" $@ $(Q) $(AM_AR) $@ $^ $(E) "RANLIB" $@ $(Q) $(RANLIB) $@ endef # 1 - dir define ProgInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_PROGRAM) $< $(1) endef # 1 - dir define ScriptInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_SCRIPT) $< $(1) endef # 1 - dir define DataInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_DATA) $< $(1) endef # 1 - dir, add manX subdir ManInstall = $(call DataInstall,$(1)/man$(call LastWord,$(subst ., ,$<))) # 1 - dir define LTLibInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(LIBTOOLCMD) --mode=install $(INSTALL) $< $(1) endef ## ## Create .srcext -> .obj mapping for a language ## # 1-tgt, 2-name, 3-srcext define LangObjTarget $(trace3) $$(OBJDIR)/$(1)/%$(OBJEXT) $$(OBJDIR)/$(1)/%.lo: %$(3) @$$(call MkDir,$$(dir $$@)) $$(AM_LANG_$(2)_COMPILE) endef # 1=tgt, 2=name define LangSetup $(trace2) $(foreach ext,$(AM_LANG_$(2)_SRCEXTS),$(call LangObjTarget,$(1),$(2),$(ext))$(NewLine)) endef ## ## Utility functions ## # for function debugging, put them at the start of body ifdef AM_TRACE trace1=$(warning $0('$1')) trace2=$(warning $0('$1','$2')) trace3=$(warning $0('$1','$2','$3')) trace4=$(warning $0('$1','$2','$3','$4')) trace5=$(warning $0('$1','$2','$3','$4','$5')) trace6=$(warning $0('$1','$2','$3','$4','$5','$6')) trace7=$(warning $0('$1','$2','$3','$4','$5','$6','$7')) trace8=$(warning $0('$1','$2','$3','$4','$5','$6','$7','$8')) trace9=$(warning $0('$1','$2','$3','$4','$5','$6','$7','$8','$9')) endif # for use inside $(eval) IFDEF = ifdef IFEQ = ifeq IFNEQ = ifneq ELSE = else ENDIF = endif # returns 'true' if $1==$2 Eq = $(if $(1)$(2),$(if $(findstring $(1),$(2)),$(if $(findstring $(2),$(1)),true)),true) Not = $(if $(1),,true) Neq = $(call Not,$(call Eq,$(1),$(2))) # replace [-./] with '_' CleanName = $(subst /,_,$(subst -,_,$(subst .,_,$(1)))) # return last word from word list LastWord = $(if $(1),$(word $(words $(1)),$(1))) Empty = Space = $(Empty) $(Empty) # twice to unconfuse syntax hiliters SQuote = ' SQuote = ' define NewLine endef # quote str for shell ShellQuote = '$(subst $(SQuote),'\$(SQuote)',$(1))' # replace extensions # 1-src ext list # 2-target ext # 3-source list ReplaceExts = $(foreach ext,$(1),$(patsubst %$(ext),%$(2),$(filter %$(ext),$(3)))) # objs with objdir from source list (1-cleantgt, 2-src list) SourceObjs = $(trace1)$(call SourceObjsExt,$(1),$(OBJEXT),$(2)) # objs with objdir from source list # 1-cleantgt, 2-objext, 3-srcs list SourceObjsExt = $(addprefix $(call JoinPath,$(OBJDIR),$(1))/, $(call ReplaceExts,$(AM_SRCEXTS),$(2),$(3))) # dependency files from object files, must match OBJDEPS DepFiles = $(sort $(wildcard $(addsuffix .d,$(1)))) # per-target var override, 1=target, 2=varname # if foo_VAR exists, expand to: # build_foo install_foo clean_foo: AM_VAR = $(foo_VAR) # 1-tgt, 2-var, 3-final TgtVar2 = $(3): AM_$(2) = $$($(1)_$(2))$(NewLine) TgtVar = $(if $($(1)_$(2)),$(call TgtVar2,$(1),$(2),$(3))) # loop TgtVar over AM_TARGET_VARIABLES, 1=target, 2-final VarOverride = $(foreach var,$(AM_TARGET_VARIABLES),$(call TgtVar,$(1),$(var),$(2))) # check if actual target (.h, .exe) is nodist based on primary and flags # 1-prim 2-flags TargetNoDist = $(strip $(if $(filter nodist,$(2)), \ true, \ $(if $(filter dist,$(2)), \ , \ $(filter-out $(AM_DIST_PRIMARIES),$(1))))) # return sources that match language # 1-lang # 2-sources LangFiles = $(filter $(addprefix %,$(AM_LANG_$(1)_SRCEXTS)),$(2)) # return list of langs that match sources. # 1-sources LangList = $(strip $(foreach lang,$(AM_LANGUAGES),$(if $(call LangFiles,$(lang),$(1)),$(lang)))) # 1-sources LinkLangList = $(foreach lang,$(call LangList,$(1)),$(if $(AM_LANG_$(lang)_LINK),$(lang))) # pick linker variable based on sources, fallback to C # 1-sources DetectLinkVar = AM_LANG_$(call LastWord,C $(call LinkLangList,$(1)))_LINK # convert 'foo/bar' -> '../..' UpDirStep1 = $(subst /, ,$(1)) UpDirStep2 = $(foreach dir,$(call UpDirStep1,$(1)),../) UpDirStep3 = $(subst / ,/,$(call UpDirStep2,$(1))) UpDirStep4 = $(patsubst %/,%,$(call UpDirStep3,$(1))) UpDir = $(if $(filter-out .,$(1)),$(call UpDirStep4,$(1)),.) # # AntiMake requires that joining clean names must result in clean names. # # Thus: # JoinPath(.,foo) -> foo # JoinPath(foo,/abs) -> /abs # JoinPath(a/b,../c) -> a/c # JoinPath(a,../../b/c) -> ../b/c # # 1-path, 2-last name : foo => . | /foo => / | foo/bar => foo CutLastName = $(if $(filter $(2),$(1)),.,$(if $(filter /$(2),$(1)),/,$(patsubst %/$(2),%,$(1)))) # 1-path component, remove last elem : CutLast = $(call CutLastName,$(1),$(lastword $(subst /, ,$(1)))) # 1/2 : actual place where / is put JoinPathFinal = $(if $(filter /,$(1)),$(1)$(2),$(1)/$(2)) # 1/2 : second starts with ../, remove it and last component of $(1) JoinPath5 = $(call JoinPath,$(call CutLast,$(1)),$(patsubst ../%,%,$(2))) # 1/2: check if first ends with .. JoinPath4 = $(if $(filter .. %/..,$(1)),$(call JoinPathFinal,$(1),$(2)),$(call JoinPath5,$(1),$(2))) # 1/2 : check if second starts with ..; otherwise join JoinPath3 = $(if $(filter ../%,$(2)),$(call JoinPath4,$(1),$(2)),$(call JoinPathFinal,$(1),$(2))) # 1/2 : skips component if '.' JoinPath2 = $(if $(filter-out .,$(1)),$(if $(filter-out .,$(2)),$(call JoinPath3,$(1),$(2)),$(1)),$(2)) # 1/2 : check if b is absolute, otherwise fix minor problems JoinPath = $(trace2)$(if $(filter /%,$(2)),$(2),$(call JoinPath2,$(if $(filter /,$(1)),$(1),$(patsubst %/,%,$(1))),$(patsubst ./%,%,$(2)))) ## ## Parse target list variables ## ## pick out components from name, call function # 1-varname, 2-words, 3-func, 4-func arg # func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg ParseName = $(call $(3),$(1),$(filter $(AM_PRIMARIES),$(2)),$(filter $(AM_DESTINATIONS),$(2)),$(filter $(AM_FLAGS),$(2)),$(4)) ForEachList = $(foreach var,$(2),$(call ParseName,$(var),$(subst _, ,$(var)),$(1),$(3))) ## try reconstruct name, if fails, its a random variable # 1-var, 2-prim,3-dest,4-flags CheckName = $(if $(call Eq,$(subst _, ,$(1)),$(strip $(4) $(call LastWord,$(3)) $(call LastWord,$(2)))),$(1)) ## also check if variable is filled # 1-var, 2-prim,3-dest,4-flags CheckNameFull = $(if $(call CheckName,$(1),$(2),$(3),$(4)),$(if $($(1)),$(1))) ## ## Loop over targets in list variables ## ## call function on parsed target # 1-var, 2-prim, 3-dest, 4-flags, 5-func # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags ForEachTarget2 = $(foreach tgt,$($(1)),$(call $(5),$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) ## ForEachTarget: call function on all targets in lists # 1-func, 2- var list # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags ForEachTarget = $(call ForEachList,ForEachTarget2,$(2),$(1)) ## EMBED_SUBDIRS relocations ## add subdir to files # 1-subdir, 2-file list RelocFiles = $(foreach f,$(2),$(if $(filter -%,$(f)),$(f),$(call JoinPath,$(1),$(f)))) # 1-dir, 2-pfx, 3-full RelocOneFlag2 = $(2)$(call JoinPath,$(1),$(patsubst $(2)%,%,$(3))) # 1-dir, 2-flag RelocOneFlag = $(if $(filter -L%,$(2)), \ $(call RelocOneFlag2,$(1),-L,$(2)), \ $(if $(filter -I%,$(2)), \ $(call RelocOneFlag2,$(1),-I,$(2)), \ $(2))) ## Relocate relative files, relative -I/-L, ignore -* # 1-dir, 2- flaglist RelocFlags = $(strip $(if $(filter-out .,$(1)), \ $(foreach flg,$(2),$(call RelocOneFlag,$(1),$(flg))), \ $(2))) ## Separate build dir relocation ## non-local source dir: -Isrc/include -> -Isrc/include -I$(srcdir)/src/include # 1-srcdir, 2-flag list FixIncludes = $(strip $(if $(filter-out .,$(1)), \ $(foreach flg,$(2),$(call FixIncludes2,$(1),$(flg))), \ $(2))) # 1-dir, 2-flg FixIncludes2 = $(if $(filter -I%,$(2)), \ $(call FixIncludes3,$(1),$(patsubst -I%,%,$(2))), \ $(2)) # 1-dir, 2-orig dir FixIncludes3 = -I$(2) -I$(call JoinPath,$(srcdir),$(2)) ## ## Makefile fragments ## ### fill values # abs_top_srcdir, abs_top_builddir # nosub_top_builddir, nosub_top_srcdir # 1 - subdir define SetDirs abs_builddir := $$(call JoinPath,$$(abs_top_builddir),$(1)) abs_srcdir := $$(call JoinPath,$$(abs_top_srcdir),$(1)) top_builddir := $$(call UpDir,$(1)) top_srcdir := $$(call JoinPath,$$(top_builddir),$$(nosub_top_srcdir)) builddir := . $(IFEQ) ($$(nosub_top_srcdir),$$(nosub_top_builddir)) srcdir := . $(ELSE) srcdir := $$(call JoinPath,$$(top_srcdir),$(1)) $(ENDIF) endef ## ## Embedded subdirs ## # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags define RelocBigTarget $(trace5) # move vars: $(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$$(am_PFX)_$(1)_$(var) := $$($(1)_$(var))) # move and relocate EXTRA_$$(am_PFX)_$(1)_SOURCES := $$(call RelocFiles,$$(am_DIR),$$(EXTRA_$(1)_SOURCES)) $$(am_PFX)_$(1)_SOURCES := $$(call RelocFiles,$$(am_DIR),$$($(1)_SOURCES)) $$(am_PFX)_$(1)_DEPENDENCIES := $$(call RelocFiles,$$(am_DIR),$$($(1)_DEPENDENCIES)) $$(am_PFX)_$(1)_LDADD := $$(call RelocFiles,$$(am_DIR),$$($(1)_LDADD)) $$(am_PFX)_$(1)_LIBADD := $$(call RelocFiles,$$(am_DIR),$$($(1)_LIBADD)) $$(am_PFX)_$(1)_CFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CFLAGS)) $$(am_PFX)_$(1)_CPPFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CPPFLAGS)) $$(am_PFX)_$(1)_LDFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_LDFLAGS)) # clean vars $(1)_SOURCES = $(1)_LDADD = $(1)_LIBADD = $(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$(1)_$(var) = ) endef ## pick actual func # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags define RelocTarget $(trace5) $(if $(filter $(AM_BIG_PRIMARIES),$(3)),$(call RelocBigTarget,$(1),$(2),$(3),$(4),$(5))) endef ## relocate target list # func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg define RelocTList $(trace5) # detect top and subdir target conflict - it's easier to detect # and error out than to work around the rare case $(IFNEQ) (,$$(filter $(2),$$(AM_BIG_PRIMARIES))) $(IFEQ) (.,$$(am_DIR)) am_TOP_NAMES += $$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))) $(ELSE) $(IFNEQ) (,$$(filter $$(am_TOP_NAMES),$$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))))) $$(error $$(NewLine)$$(NewLine)\ *** Target names used in top Makefile cannot be re-used in embedded Makefiles. $$(NewLine)\ *** The target variables (eg. _SOURCES) conflict is not handled yet) $(ENDIF) $(ENDIF) $(ENDIF) # move value under real_% $(IFEQ) ($(real_$(1)),) real_$(1) := $(ENDIF) real_$(1) += $$(call RelocFiles,$$(am_DIR),$$($(1))) $(1) = # remember in proper list $(IFEQ) ($(3),EXTRA) am_EXTRA_TARGETLISTS += real_$(1) $(ELSE) am_TARGETLISTS += real_$(1) $(ENDIF) endef ## process included values # 1-dir, 2-pfx, 3-tlist define EmbedProcess $(trace3) $(IFNEQ) ($$(filter $(1),$$(am_EMBED_DONE)),) $$(error Double entry in EMBED_SUBDIRS: $(1)) $(ENDIF) # init local vars am_DIR := $(1) am_LOC := $$(call JoinPath,$$(SUBLOC),$(1)) am_PFX := $(2) am_EMBED_DONE += $(1) # reloc & save vars am_DISTFILES += $$(call RelocFiles,$$(am_DIR),$$(EXTRA_DIST)) am_CLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(CLEANFILES)) am_DISTCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(DISTCLEANFILES)) am_MAINTAINERCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(MAINTAINERCLEANFILES)) am_EMBED_TODO += $$(call RelocFiles,$$(am_DIR),$$(EMBED_SUBDIRS)) am_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(SUBDIRS)) am_DIST_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(DIST_SUBDIRS)) # clean vars for new dir EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = EMBED_SUBDIRS = SUBDIRS = DIST_SUBDIRS = $(call SetDirs,$(call JoinPath,$(SUBLOC),$(1))) $(call ForEachTarget,RelocTarget,$(3)) $(call ForEachList,RelocTList,$(3)) endef ## read Makefile.am, process it # 1 - dir DoEmbed = $(trace1)$(strip \ $(if $(wildcard $(am_srcdir)/$(1)/Makefile.am), \ $(eval include $(am_srcdir)/$(1)/Makefile.am $(NewLine)) \ $(eval $(call EmbedProcess,$(1),$(call CleanName,$(1)),$(AM_NONEXTRA_TLISTS) $(AM_EXTRA_TLISTS))), \ $(error $(SUBLOC)/Makefile failure: $(call JoinPath,$(SUBLOC),$(1)/Makefile.am) not found.))) ## ## Fragments that build targets ## # Note that variable initialization order is important here # as some of them will be used immediately. ## ## Install target object ## # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define InstallTarget $(trace5) $(1)_DEST := $$(if $$($(4)dir),$$($(4)dir),$$(error '$(4)dir' empty))$(if $(filter nobase,$(5)),/$(dir $(2))) $(1)_InstFunc := $$(if $$(AM_$(3)_InstFunc),$$(AM_$(3)_InstFunc),DataInstall) # actual installation .PHONY: install_$(1) install: install_$(1) install_$(1): $(2) $$(call $$($(1)_InstFunc),$$(DESTDIR)$$($(1)_DEST)) # hack to pass -rpath to LTLIBRARIES on build time (1) $(2): AM_DEST = $$($(1)_DEST) # simple uninstall - just remove files .PHONY: uninstall_$(1) uninstall: uninstall_$(1) uninstall_$(1): $$(RM) $$(DESTDIR)$$($(1)_DEST)/$$(notdir $(2)) endef # hack to pass -rpath to LTLIBRARIES on build time (2) %.la: AM_LT_RPATH = $(if $(AM_DEST),-rpath $(AM_DEST)) ## ## Rules for big target ## # 1-varname, 2-ifset, 3-ifnotset IfSet = $(if $(filter-out undefined,$(flavor $(1))),$(2),$(3)) # 1-clean, 2-raw, 3-prim PROGRAMS_Final = $(if $($(1)_EXT),$(2)$($(1)_EXT),$(2)$(EXEEXT)) # 1-clean, 2-raw, 3-prim LIBRARIES_Final = $(if $($(1)_EXT),$(2)$($(1)_EXT),$(patsubst %.a,%$(LIBEXT),$(2))) # calculate target file name # 1-clean, 2-raw, 3-prim FinalTargetFile = $(call IfSet,$(3)_Final,$(call $(3)_Final,$(1),$(2),$(3)),$(2)$($(1)_EXT)) # 1-objs FixObjs = $(patsubst %.a,%$(LIBEXT),$(1)) # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define BigTargetBuild $(trace5) AM_ALL_TARGETS += $(1) $(1)_ALLSRCS := $$($(1)_SOURCES) $$(EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) # calculate OBJS from SOURCES $(1)_OBJEXT := $$(if $$(AM_$(3)_OBJEXT),$$(AM_$(3)_OBJEXT),$$(OBJEXT)) $(1)_OBJS := $$(call SourceObjsExt,$(1),$$($(1)_OBJEXT), \ $$($(1)_SOURCES) $$(nodist_$(1)_SOURCES)) $(1)_OBJS_CLEAN := $$($(1)_OBJS) # include additional objects, move flags to _LIBS $(IFEQ) ($(3),PROGRAMS) $(1)_OBJS += $$(filter-out -%,$$($(1)_LDADD)) $(1)_LIBS += $$(filter -%,$$($(1)_LDADD)) $(ELSE) $(1)_OBJS += $$(filter-out -%,$$($(1)_LIBADD)) $(1)_LIBS += $$(filter -%,$$($(1)_LIBADD)) $(ENDIF) # autodetect linker, unless given $(IFEQ) ($($(1)_LINK),) $(1)_LINKVAR := $$(call DetectLinkVar,$$($(1)_ALLSRCS)) $(ELSE) $(1)_LINKVAR := $(1)_LINK $(ENDIF) # calculate target file name $(1)_FINAL = $(call FinalTargetFile,$(1),$(2),$(3)) # hook libtool into LTLIBRARIES cleanup $(IFEQ) ($(3),LTLIBRARIES) $(1)_RM = $$(LTCLEAN) $$(RM) $(ELSE) $(1)_RM = $$(RM) $(ENDIF) # fix includes in case of separate build dir $(1)_CPPFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CPPFLAGS)) $(1)_CFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CFLAGS)) # load dependencies -include .dummy. $$(call DepFiles, $$($(1)_OBJS)) # actual build, clean & install targets .PHONY: build_$(1) clean_$(1) # allow target-specific variables $$(eval $$(call VarOverride,$(1),$(call FinalTargetFile,$(1),$(2),$(3)))) # build and clean by default, unless flagged EXTRA $(IFNEQ) ($(4),EXTRA) all: build_$(1) $(ENDIF) clean: clean_$(1) # _DEPENDENCIES and nodist_SOURCES must exist before build starts. $$(call FixObjs,$$($(1)_OBJS)): $$($(1)_DEPENDENCIES) $$(nodist_$(1)_SOURCES) build_$(1): $$($(1)_FINAL) $$($(1)_FINAL): $$(call FixObjs,$$($(1)_OBJS)) @$$(call MkDir,$$(dir $$@)) $$($(if $(filter LIBRARIES,$(3)),ar_lib,$$($(1)_LINKVAR))) clean_$(1): $$(E) "CLEAN" "$$($(1)_FINAL)" $$(Q) $$($(1)_RM) -- $$($(1)_OBJS_CLEAN) $(if $(call TargetNoDist,$(3),$(5)),$$($(1)_FINAL)) DISTCLEANFILES += $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) $(foreach lang,$(AM_LANGUAGES),$(call LangSetup,$(1),$(lang))) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define BigTargetDist am_DISTFILES += $$(filter-out $$(nodist_EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES),$$($(1)_SOURCES) \ $$(EXTRA_$(1)_SOURCES)) $(if $(call TargetNoDist,$(3),$(5)),,$$($(1)_FINAL)) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define MakeBigTarget $(trace5) # build if first time $(IFEQ) ($(filter $(1),$(AM_ALL_TARGETS)),) $(call BigTargetBuild,$(1),$(2),$(3),$(4),$(5)) $(call BigTargetDist,$(1),$(2),$(3),$(4),$(5)) $(ELSE) # allow only EXTRA be double $(IFNEQ) ($(4),EXTRA) $$(error Target '$2' described listed several times) $(ENDIF) $(ENDIF) # call InstallTarget, for dest != (EXTRA, noinst) $(IFEQ) ($(filter EXTRA noinst,$(4)),) $(call InstallTarget,$(1),$$($(1)_FINAL),$(3),$(4),$(5)) $(ENDIF) endef ## ## Rules for small target ## # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define MakeSmallTarget $(trace5) AM_ALL_TARGETS += $(1) # should the target file be distributed or cleaned? $(IFEQ) ($(call TargetNoDist,$(3),$(5)),) am_DISTFILES += $(2) $(ELSE) CLEANFILES += $(2) $(ENDIF) # build if not EXTRA $(IFNEQ) ($(4),EXTRA) all: $(2) # install if not EXTRA or noinst $(IFNEQ) ($(4),noinst) $(call InstallTarget,$(1),$(2),$(3),$(4),$(5)) $(ENDIF) $(ENDIF) endef ## ## Fill GNU-style vars for subdir ## # preferred to top_srcdir/top_builddir topdir = $(top_builddir) ifneq ($(nosub_top_builddir),.) $(error Non-local builddir not supported) endif # initial locaton vars $(eval $(call SetDirs,$(SUBLOC))) ifneq ($(nosub_top_srcdir),$(nosub_top_builddir)) # use VPATH to find non-local sources VPATH += $(srcdir) # fix includes AM_CPPFLAGS := $(call FixIncludes,$(srcdir),$(AM_CPPFLAGS)) AM_CFLAGS := $(call FixIncludes,$(srcdir),$(AM_CFLAGS)) endif ## ## O= ## if given, create wrapper makefiles in target dir ## that include makefiles from source dir, then run ## make from target dir. ## ifneq ($(O),) # 1-makefile define WrapMakeFileCmd @$(call MkDir,$(dir $(O)/$(1))) @$(call Printf,'%s\n%s\n%s\n%s\n%s\n', \ 'abs_top_srcdir = $(CURDIR)' \ 'abs_top_builddir = $(call JoinPath,$(CURDIR),$(O))' \ 'nosub_top_srcdir = $(call UpDir,$(O))' \ 'nosub_top_builddir = .' \ 'include $(abs_top_srcdir)/$(1)') \ > $(O)/$(1) endef # 1-makefile WrapMakeFile = $(if $(wildcard $(O)/$(1)),,$(call WrapMakeFileCmd,$(1))$(NewLine)) # redirect whatever rule was given .PHONY: all $(MAKECMDGOALS) all $(filter-out all,$(MAKECMDGOALS)): $(if $(wildcard $(O)),,$(error O=$(O): Directory '$(O)' does not exist)) $(foreach mk,$(filter-out /%,$(MAKEFILE_LIST)),$(call WrapMakeFile,$(mk))) $(Q) $(MAKE) O= -C $(O) $(MAKECMDGOALS) # O=empty, this is main makefile else ## ## main targets, tie them with subdir and local targets ## # disable random rules .SUFFIXES: all: sub-all all-local clean: sub-clean clean-local install: sub-install install-local uninstall: sub-uninstall uninstall-local distclean: sub-distclean distclean-local maintainer-clean: sub-maintainer-clean maintainer-clean-local .PHONY: all clean install dist distclean maintainer-clean # -local are empty targets by default .PHONY: all-local clean-local install-local uninstall-local distclean-local maintainer-clean-local all-local clean-local install-local uninstall-local distclean-local maintainer-clean-local: ## ## Actual embedding starts ## AM_ALL_TLISTS2 = $(filter $(addprefix %,$(AM_PRIMARIES)),$(.VARIABLES)) AM_ALL_TLISTS = $(call ForEachList,CheckName,$(AM_ALL_TLISTS2)) AM_NONEXTRA_TLISTS = $(filter-out EXTRA_%,$(AM_ALL_TLISTS)) AM_EXTRA_TLISTS = $(filter EXTRA_%,$(AM_ALL_TLISTS)) am_srcdir := $(srcdir) am_DIR := . am_PFX := am_TARGETLISTS := am_EXTRA_TARGETLISTS := am_TOP_NAMES := # move top-level targets away $(eval $(call ForEachList,RelocTList,$(AM_NONEXTRA_TLISTS))) $(eval $(call ForEachList,RelocTList,$(AM_EXTRA_TLISTS))) am_SUBDIRS := $(SUBDIRS) am_DIST_SUBDIRS := $(DIST_SUBDIRS) am_DISTFILES := $(EXTRA_DIST) am_CLEANFILES := $(CLEANFILES) am_DISTCLEANFILES := $(DISTCLEANFILES) am_MAINTAINERCLEANFILES := $(MAINTAINERCLEANFILES) am_EMBED_NOW := $(EMBED_SUBDIRS) am_EMBED_DONE := am_EMBED_TODO := EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = SUBDIRS = DIST_SUBDIRS = EMBED_SUBDIRS = $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(if $(am_EMBED_NOW),$(error EMBED_SUBDIRS recursion limit reached...)) # embedding done, move variables back $(eval $(call SetDirs,$(SUBLOC))) CLEANFILES := $(am_CLEANFILES) DISTCLEANFILES := $(am_DISTCLEANFILES) MAINTAINERCLEANFILES := $(am_MAINTAINERCLEANFILES) SUBDIRS := $(am_SUBDIRS) DIST_SUBDIRS := $(am_DIST_SUBDIRS) EMBED_SUBDIRS := $(am_EMBED_DONE) am_CLEANFILES = am_DISTCLEANFILES = am_MAINTAINERCLEANFILES = am_DIST_SUBDIRS = am_SUBDIRS = am_EMBED_DONE = am_TARGETLISTS := $(sort $(am_TARGETLISTS)) am_EXTRA_TARGETLISTS := $(sort $(am_EXTRA_TARGETLISTS)) # avoid duplicate entries with am_TARGETLISTS am_EXTRA_TARGETLISTS := $(filter-out $(am_TARGETLISTS),$(am_EXTRA_TARGETLISTS)) # allow seeing moved lists AM_FLAGS += real ## EMBED_SUBDIRS end ## ## Launch target hooks ## amdir = $(dir $(realpath $(filter %/antimake.mk antimake.mk,$(MAKEFILE_LIST)))) # 1-feat name FeatFile = $(call JoinPath,$(amdir),amext-$(1).mk) # 1- fname LoadFeature = $(if $(wildcard $(call FeatFile,$(1))),$(eval include $(call FeatFile,$(1))),$(error Feature "$(call FeatFile,$(1))" is not available.)) $(foreach f,$(AM_FEATURES),$(call LoadFeature,$(f))) $(eval $(foreach hook,$(AM_TARGET_HOOKS),$(call ForEachTarget,$(hook),$(am_TARGETLISTS)))) $(eval $(foreach hook,$(AM_TARGET_HOOKS),$(call ForEachTarget,$(hook),$(am_EXTRA_TARGETLISTS)))) ## ## Now generate the rules ## ## check which target func to call # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags MakeTarget = $(call $(if $(filter $(AM_BIG_PRIMARIES),$(3)),MakeBigTarget,MakeSmallTarget),$(1),$(2),$(3),$(4),$(5)) ## process all targets in one list # 1-list, 2-prim,3-dest,4-flags MakeTargetList = $(foreach tgt,$($(1)),$(call MakeTarget,$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) ## process all target lists # 1=list names ProcessTargets = $(call ForEachTarget,MakeTarget,$(1)) # process non-EXTRA targets $(eval $(call ProcessTargets,$(am_TARGETLISTS))) # process EXTRA_* last, they may already have been processed $(eval $(call ProcessTargets,$(am_EXTRA_TARGETLISTS))) ## ## clean targets ## clean: ifdef CLEANFILES $(E) "CLEAN" $@ $(Q) $(RM) -- $(CLEANFILES) endif distclean: clean $(E) "DISTCLEAN" $@ $(Q) $(RM) -r -- $(OBJDIR) ifdef DISTCLEANFILES $(Q) $(RM) -- $(DISTCLEANFILES) endif maintainer-clean: clean $(E) "MAINTAINERCLEAN" $@ $(Q) $(RM) -r -- $(OBJDIR) ifdef DISTCLEANFILES $(Q) $(RM) -- $(DISTCLEANFILES) endif ifdef MAINTAINERCLEANFILES $(Q) $(RM) -- $(MAINTAINERCLEANFILES) endif ## ## actual subdir targets ## # 1-dir define MakeSubDir $(trace1) $(E) "MKDIR" "Create $(call JoinPath,$(SUBLOC),$(1))" $(Q) $(call MkDir,$(1)) $(Q) $(call Printf,"include $(call UpDir,$(1))/$(srcdir)/$(1)/Makefile\n") \ > $(1)/Makefile endef # 1-dir, 2-tgt define SubTarget $(trace2) $(if $(wildcard $(1)/Makefile),,$(call MakeSubDir,$(1))) $(E) "-->" "$(call JoinPath,$(SUBLOC),$(1))" $(Q) $(MAKE) -C $(1) $(2) $(E) "<--" "$(call JoinPath,$(SUBLOC),$(1))" endef sub-all sub-install sub-uninstall sub-clean: $(foreach dir,$(SUBDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) # Avoid double dirs in DIST_SUBDIRS, without changing order am_DISTDIRS = $(SUBDIRS) $(foreach dir,$(DIST_SUBDIRS),$(if $(filter $(dir),$(SUBDIRS)),,$(dir))) sub-dist sub-distclean sub-maintainer-clean: $(foreach dir,$(am_DISTDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) .PHONY: sub-all sub-clean sub-install sub-dist sub-distclean sub-maintainer-clean ## ## actual dist targets ## DistTarget = $(foreach fmt,$(1),dist-$(fmt)) AM_DIST_ALL ?= gzip bzip2 xz zip AM_DIST_ALL_TGTS = $(call DistTarget,$(AM_DIST_ALL)) AM_DIST_DEF_TGTS = $(call DistTarget,$(AM_DIST_DEFAULT)) AM_FORMAT_gzip_EXT = tar.gz AM_FORMAT_gzip_CMD = tar chof - $(AM_DIST_BASE) | gzip > $(AM_DIST_BASE).$(AM_FORMAT_gzip_EXT) AM_FORMAT_bzip2_EXT = tar.bz2 AM_FORMAT_bzip2_CMD = tar chof - $(AM_DIST_BASE) | bzip2 > $(AM_DIST_BASE).$(AM_FORMAT_bzip2_EXT) AM_FORMAT_xz_EXT = tar.xz AM_FORMAT_xz_CMD = tar chof - $(AM_DIST_BASE) | xz > $(AM_DIST_BASE).$(AM_FORMAT_xz_EXT) AM_FORMAT_zip_EXT = zip AM_FORMAT_zip_CMD = zip -rq $(AM_DIST_BASE).$(AM_FORMAT_zip_EXT) $(AM_DIST_BASE) # 1-name define MakeDist $(E) "CHECK" $@ $(Q) $(MAKE) -s am-check-distfiles $(E) "MKDIR" $(AM_DIST_BASE) $(Q) $(RM) -r -- $(AM_DIST_BASE) $(AM_DIST_BASE).$(AM_DIST_$(1)_EXT) $(Q) $(call MkDir,$(AM_DIST_BASE)) $(E) "COPY" $(AM_DIST_BASE) $(Q) $(MAKE) -s am-show-distfiles | cpio -pmduL --quiet $(AM_DIST_BASE) $(E) "PACK" $(AM_DIST_BASE).$(AM_FORMAT_$(1)_EXT) $(Q) $(AM_FORMAT_$(1)_CMD) $(Q) $(RM) -r -- $(AM_DIST_BASE) endef .PHONY: dist $(AM_DIST_ALL_TGTS) dist: $(AM_DIST_DEF_TGTS) dist-all: $(AM_DIST_ALL_TGTS) $(AM_DIST_ALL_TGTS): $(call MakeDist,$(subst dist-,,$@)) # show list of files that need to be in final archive .PHONY: am-show-distfiles am-show-distfiles: $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) --no-print-directory -C $(dir) $@ $(NewLine)) $(foreach file,$(am_FINAL_DISTFILES),@$(call Printf,"$(call JoinPath,$(SUBLOC),$(file))\n") $(NewLine)) # do dependencies as separate step, in case building outputs anything .PHONY: am-check-distfiles am-check-distfiles: $(am_FINAL_DISTFILES) $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) -C $(dir) $@ $(NewLine)) ## ## debug target ## # 1=var define AmDebugShow $(if $($(1)),@$(call Printf,"$(1) = $($(1))\n")) $(NewLine) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define AmDebugTarget $(trace5) $(foreach var,$(AM_DEBUG_TARGET_VARS),$(call AmDebugShow,$(1)_$(var))) @$(call Printf,"\n") endef # func args: 1-var, 2-prim, 3-dest, 4-flags CollectDests = $(filter-out noinst EXTRA,$(3)) AM_USED_DESTS = $(sort $(call ForEachList,CollectDests,$(am_TARGETLISTS))) AM_DEBUG_VARS = GNUMAKE380 GNUMAKE381 GNUMAKE382 MAKEFILE_LIST \ AM_LANGUAGES AM_FLAGS AM_DESTINATIONS \ AM_ALL_TARGETS EXEEXT am_FINAL_DISTFILES \ nosub_top_builddir nosub_top_srcdir \ abs_top_srcdir abs_top_builddir \ srcdir builddir top_srcdir top_builddir \ SUBDIRS EMBED_SUBDIRS DIST_SUBDIRS \ DISTFILES CLEANFILES DISTCLEANFILES MAINTAINERCLEANFILES AM_DEBUG_TARGET_VARS = SOURCES OBJS LINKVAR DEST USUAL_OBJS USUAL_SRCS EXT FINAL \ $(AM_TARGET_VARIABLES) AM_DEBUG_LANG_VARS = SRCEXTS am-debug: @$(call Printf,"\n==== Global Variables ====\n") $(foreach var,$(AM_DEBUG_VARS),$(call AmDebugShow,$(var))) @$(call Printf,"\n==== Per-language Variables ====\n") $(foreach lg,$(AM_LANGUAGES),$(foreach var,$(AM_DEBUG_LANG_VARS),$(call AmDebugShow,AM_LANG_$(lg)_$(var)))) @$(call Printf,"\n==== Per-target Variables ====\n") $(call ForEachTarget,AmDebugTarget,$(am_TARGETLISTS) $(am_EXTRA_TARGETLISTS)) @$(call Printf,"\n==== Active install directories ====\n") $(foreach dst,$(AM_USED_DESTS),@$(call Printf," $(dst)dir = $($(dst)dir)\n" $(NewLine))) ## ## regtests for basic tools ## AM_TESTS = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 AM_TEST_1 = $(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a) AM_TEST_1_RES = true,true,, AM_TEST_2 = $(call Neq,a,aa),$(call Neq,a,a) AM_TEST_2_RES = true, AM_TEST_3 = $(call CleanName,obj/foo-baz.x) AM_TEST_3_RES = obj_foo_baz_x AM_TEST_4 = $(call LastWord,a),$(call LastWord,a b c),$(call LastWord,) AM_TEST_4_RES = a,c, AM_TEST_5 = $(call ReplaceExts,.c .cpp X.foo,.o,s1.c s2.cpp s3X.foo s4.h) AM_TEST_5_RES = s1.o s2.o s3.o AM_TEST_5 = $(call LangList,foo.c c.foo),$(call LangList,foo.c c.foo f.cpp) AM_TEST_5_RES = C,C CXX AM_TEST_6 = $(call DetectLinkVar,foo.c c.foo),$(call DetectLinkVar,foo.c c.foo x.cpp),$(call DetectLinkVar,foo),$(call DetectLinkVar,) AM_TEST_6_RES = AM_LANG_C_LINK,AM_LANG_CXX_LINK,AM_LANG_C_LINK,AM_LANG_C_LINK AM_TEST_7 = $(call UpDir,foo)|$(call UpDir,)|$(call UpDir,.)|$(call UpDir,foo/bar)|$(call UpDir,a/b/c)| AM_TEST_7_RES = ..|.|.|../..|../../..| AM_TEST_8 = $(call JoinPath,.,.)|$(call JoinPath,,)|$(call JoinPath,a,.)|$(call JoinPath,.,b)|$(call JoinPath,a,b)|$(call JoinPath,a/b,../c)|$(call JoinPath,a/b,../../../c) AM_TEST_8_RES = .||a|b|a/b|a/c|../c define AM_TEST_9_EVAL $(IFEQ) ($$(AM_TEST_9_RES),OK) AM_TEST_9 = OK $(ELSE) AM_TEST_9 = fail $(ENDIF) endef AM_TEST_9_RES = OK $(eval $(AM_TEST_9_EVAL)) AM_TEST_10 = $(call CheckName,nobase_bin_PROGRAMS,PROGRAMS,bin,nobase)|$(call CheckName,a,a,,)|$(call CheckName,bin_bin_DATA,,bin bin,DATA) AM_TEST_10_RES = nobase_bin_PROGRAMS|a| AM_TEST_11_Show = $(4)-$(3)-$(2) AM_TEST_11 = $(call ForEachList,AM_TEST_11_Show,bin_PROGRAMS foo_DATA baz_foo base_nobase_dist_nodist_DATA_PROGRAMS) AM_TEST_11_RES = -bin-PROGRAMS --DATA -- base nobase dist nodist--DATA PROGRAMS AM_TEST_12 = $(call RelocFlags,sub/dir,-I. -I./foo -Lfoo/bar -I/inc -L/lib -lfoo) AM_TEST_12_RES = -Isub/dir -Isub/dir/foo -Lsub/dir/foo/bar -I/inc -L/lib -lfoo AM_TEST_13 = $(call TargetNoDist,HEADERS,)|$(call TargetNoDist,HEADERS,nodist)|$(call TargetNoDist,PROGRAMS,)|$(call TargetNoDist,PROGRAMS,dist) AM_TEST_13_RES = |true|PROGRAMS| AM_TEST_14 = $(call ShellQuote,foo'bar\')|$(call ShellQuote,as!d' \\ $$foo) AM_TEST_14_RES = 'foo'\''bar\'\'''|'as!d'\'' \\ $$foo' AM_TEST_15 = $(call JoinPath,sub/dir,../foo) , \ $(call JoinPath,sub/dir,../../foo) , \ $(call JoinPath,sub/dir,../../../foo) , \ $(call JoinPath,sub/dir/,../foo) , \ $(call JoinPath,/,./foo) , \ $(call JoinPath,..,../foo) , \ $(call JoinPath,/foo,../baz) , \ $(call JoinPath,/foo,../../baz) , \ $(call JoinPath,foo/..,./foo) AM_TEST_15_RES = sub/foo , foo , ../foo , sub/foo , /foo , ../../foo , /baz , /baz , foo/../foo AM_TEST_16_EXT = .foo AM_TEST_16 = $(call FinalTargetFile,prog,prog,PROGRAMS) | $(call FinalTargetFile,AM_TEST_16,AM_TEST_16,PROGRAMS) AM_TEST_16_RES = prog$(EXEEXT) | AM_TEST_16.foo AmTest = $(if $(call Eq,$($(1)),$($(2))),@$(call Printf,"$(1): OK\n"),@$(call Printf,"$(subst ",',$(1): FAIL: $($(1)) != $($(2))\n)"))$(NewLine) am-test: $(Q) test "$(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a)" = "true,true,," $(foreach nr,$(AM_TESTS),$(call AmTest,AM_TEST_$(nr),AM_TEST_$(nr)_RES)) ## ## help target ## AmHelpNames = targets standalone internal config dests .PHONY: help $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) $(foreach n,$(AmHelpNames),help-$(n)-local): help: $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) # 1-var, 2-desc AmConf = @$(call Printf," %-27s %s=%s\n" $(call ShellQuote,$(2)) $(call ShellQuote,$(1)) $(call ShellQuote,$($(1)))) help-targets: @$(call Printf,"\n") @$(call Printf,"Main targets:\n") @$(call Printf," all Build all targets (default)\n") @$(call Printf," install Install files\n") @$(call Printf," dist Create source archive\n") @$(call Printf," clean Clean built files\n") @$(call Printf," distclean Clean configured files\n") @$(call Printf," maintainer-clean Delete anything that can be generated\n") help-standalone: @$(call Printf,"\n") @$(call Printf,"Standalone targets: (make -f antimake.mk)\n") @$(call Printf," show-location Prints full path to antimake.mk (default)\n") @$(call Printf," show-config Prints template config.mak.in\n") help-internal: @$(call Printf,"\n") @$(call Printf,"Internal targets:\n") @$(call Printf," am-show-distfiles Shows files that go into source archive\n") @$(call Printf," am-debug Shows variables that affect the build\n") @$(call Printf," am-test Regtest for internal functions\n") help-config: @$(call Printf,"\n") @$(call Printf,"Config variables and their current values:\n") $(call AmConf,CC,C compiler) $(call AmConf,CFLAGS,C compiler flags) $(call AmConf,CPPFLAGS,C pre-processor flags) $(call AmConf,LDFLAGS,Linker flags) help-dests: @$(call Printf,"\n") @$(call Printf,"Destinations for install [ prefix=$(prefix) ]:\n") $(foreach dst,$(AM_USED_DESTS),@$(call Printf," $(dst)dir = $($(dst)dir)\n") $(NewLine)) endif # O=empty pgqd/lib/mk/antimake.txt0000664000401600040160000003216313175113172013570 0ustar cbecbe= antimake.mk(5) = == NAME == antimake - Minimal Automake syntax on plain GNU Make == DESCRIPTION == Antimake makes possible to use GNU Automake conventions to describe builds in ordinary Makefiles for GNU Make. It's main abstractions are target lists and target variables. Target list describes target type and where to install. Target variables give source files and additional flags for build. == EXAMPLE == ------------------- # target list bin_PROGRAMS = prog # target variables for 'prog' prog_SOURCES = prog.c prog.h prog_LDADD = libutil.a # target list noinst_LIBRARIES = libutil.a # target variables for 'libutil.a' libutil_a_SOURCES = util.c util.h # load Antimake include antimake.mk ------------------- == Terminology == Primary:: target type, describes how to build and install particular type of targets. Target:: a file that needs to be built and/or installed. Distribute:: Include file in source .tar.gz. Non-distributed files are skipped when building .tar.gz and are cleaned during `make distclean`. Source:: Source files are files that appear in `..._SOURCES` per-target variable. They are distributed by default. They may or may not result in object files. It's fine to put both `.h` and `.c` files into _SOURCES. == TARGET LISTS == Target lists are variables that contain file names that need to be built and installed. They are specially named so that the name also describes how they are built, how and where they will be installed. The target list name contains 3 parts, separated with underscore, in following order: 1. Optional flags. Flags are: `nodist`, `dist`, `nobase`, `base`. (Default: `base`, `nodist`) 2. Destination directory name. Destination directory called *bin* actual location is stored in Make variable `$(bindir)`. Some common values: `bin`, `lib`, `include`. There are more and the list can be extended. Special name `noinst` means the target file should not be installed. 3. Target type, also called "primary". This will describe how the target needs to be built. Common values: `PROGRAMS`, `LIBRARIES`, `DATA` For details, what the various values mean, see next sections. .Examples: ---------------- bin_PROGRAMS = prog1 prog2 # flags: base, nodist # dest: $(bindir) # type: PROGRAMS noinst_LIBRARIES = lib1.a lib2.a # flags: base, nodist # dest: noinst # type: LIBRARIES nobase_dist_doc_DATA = docs/README # flags: dist, nobase # dest: $(docdir)/docs # type: DATA ---------------- === Primaries === `PROGRAMS`:: executable programs, linked together from objects built from source files `LIBARIES`:: static libraries, linked together from objects built from source files `LTLIBRARIES`:: dynamic or static libraries, linked together from objects built from source files `HEADERS`:: header files, no default build method, the target files have `dist` flag by default. `MANS`:: man pages, no default build method, installed into manX subdir. `SCRIPTS`:: scripts, executable file, no default build method `DATA`:: data, non-executable file, no default build method === Target list flags === `dist`:: The target should be distributed with other sources. Default for `HEADERS` type, others have `nodist` by default. `nodist`:: Target is not distributed and should be cleaned with distclean. Default for all primaries, except `HEADERS`. `base`:: On install relative path is ignored, all files end up in destination directory. Always default. `nobase`:: On install relative path is kept. Eg: if `includedir=/usr/include` then `nobase_include_HEADERS=mylib/common.h` is installed to `/usr/include/mylib/common.h`. `noinst`:: Target is built as part of build process, but is not installed. `EXTRA`:: Targets in such list are not built, nor installed. Useful to make sure that sources for dynamically configured targets will end up in source tarball. Unlike other target list ariables, `EXTRA_` may contain targets already defined in other target lists, they will be filtered out from this list then. == Target variables == Only big targets take additional variables: `PROGRAMS`/`LIBRARIES`/`LTLIBRARIES`. `_SOURCES`:: All source files, *.c *.h *.cpp *.hpp. `nodist__SOURCES`:: Source files that should not be distributed. `EXTRA__SOURCES`:: In case tgt_SOURCES is dynamic, here is non-dynamic list of sources for distribution. Only dynamic sources need to be listed here. `_DEPENDENCIES`:: Add dependencies that need to be build before target build will start. `_CFLAGS`, `_CPPFLAGS`, `_LDFLAGS`, `_LIBTOOLFLAGS`:: Override corresponging AM_xx variable `_LDADD`:: Add dependencies that are used during linking. For PROGRAMS only. They will be added to linker command line. `_LIBADD`:: Add dependencies that are used during linking. For LIBRARIES/LTLIBRARIES only. They will be added to linker command line. `_AR`:: Overrides $(AR) $(ARFLAGS). For LIBRARIES only. .Example: ------------------- bin_PROGRAMS = prog prog_SOURCE = main.c util.c util.h prog_CFLAGS = $(GTK_CFLAGS) prog_LDADD = $(GTK_LIBS) ------------------- == Global variables == They can be set before `antimake.mk` inclusion to change build behaviour. EXTRA_DIST:: Additional files to include in source archive. CLEANFILES:: Additional files to `make clean`. DISTCLEANFILES:: Additional files to `make distclean`. MAINTAINERCLEANFILES:: Additional files to `make maintainer-clean`. SUBDIRS:: Subdirectories of current directory where Make needs to be recursively launched. If subdirectory `Makefile` is Antimake-base, it should set `SUBLOC`. SUBLOC:: Current diretory location in overall source tree. This can stay unset in top directory. Needed for subdirectiories entered with `SUBDIRS` to find its position in source tree. DIST_SUBDIRS:: Subdirs that only `make dist`, `make distclean` and `make maintainer-clean` will enter. EMBED_SUBDIRS:: Subdirectories that are built non-recursively: they need to contain `Makefile.am` that contains makefile-fragment with Antimake syntax that describes local targets using relative filenames. The fragment is included in main makefile and file and variable names are converted and merged with top-level targets. AM_FEATURES:: List of extensions to load. Extensions are Makefile fragments that are loaded before actual rules are generated, so they can change or add targets. === More details on EMBED_SUBDIRS === It acts like `include $(dir)/Makefile.am` for each directory, except it converts file and variable names. Example: --------------------- Makefile: EMBED_SUBDIRS = src src/Makefile.am: bin_PROGRAMS = hello hello_SOURCES = main.c hello_CPPFLAGS = -I./include --------------------- Conversion results as if top-level `Makefile` had contained following rows: ---------------------- bin_PROGRAMS += src/hello src_hello_SOURCES = src/main.c src_hello_CPPFLAGS = -I./src/include ---------------------- Variables, where file names are converted: * SUBDIRS, DIST_SUBDIRS, EMBED_SUBDIRS * DISTFILES, CLEANFILES, DISTCLEANFILES, MAINTAINERCLEANFILES * target lists * _SOURCES, _LDADD, _LIBADD Variables, where -L and -I flags are converted: * _CFLAGS * _CPPFLAGS * _LDFLAGS Makefile should be written in a way that those conversions would be enough. === Global variables for current location === * srcdir, builddir - relative path to source dir and build dir. * top_srcdir, top_builddir - relative path to top-level source and build dir. * abs_srcdir, abs_builddir - absolute path to source and build dir * abs_top_srcdir, abs_top_builddir - absolute path to top-level source and build dir * nosub_top_srcdir, nosub_top_builddir - relative path from top of builddir to srcdir and builddir. === Global variables that target can override === - AM_CPPFLAGS - AM_CFLAGS - AM_LDFLAGS - AM_LIBTOOLFLAGS - AM_DEFS - AM_MAKEFLAGS === Global variables from autoconf === These variables come usually from autoconf, but also have reasonable defaults: CC, DEFS, CPPFLAGS, CFLAGS, LDFLAGS, LIBS, LIBTOOL, LIBTOOLFLAGS, AR, ARFLAGS, RANLIB, CXX, CXXFLAGS, INSTALL, MKDIR_P, LN_S === Global variables for extending Antimake === AM_DIST_DEFAULT:: Default format(s) for `make dist` target. One or more of: `gzip`, `bzip2`, `xz`, `zip`. Default: `gzip`. AM_DESTINATIONS:: Additional directory names to consider as valid destinations. Expects corresponding `dir`-variable to be set. AM_SMALL_PRIMARIES:: Additional single-file primaries. (Builtin: HEADERS, SCRIPTS, DATA, MANS) AM_BIG_PRIMARIES:: Additional primaries built from objects. (Builtin: PROGRAMS, LIBRARIES, LTLIBRARIES) AM_LANGUAGES:: Additional language names. Antimake expects variables `AM_LANG_$(name)_SRCEXTS`, `AM_LANG_$(name)_COMPILE` and `AM_LANG_$(name)_LINK` to be set. === Variables for command-line usage === DESTDIR:: Relocate installation root. AM_TRACE:: Turns on function-call debug info. Can be set from command-line. === Hacking variables === GNUMAKE380, GNUMAKE381, GNUMAKE382:: If we have at least that version of GNU Make. GNUMAKE380 is always set, others may not be. If Makefile uses features from newer GNU Make it would be good idea to use those flags and error out with clear error message, instead having mysterious failures. === Libtool flags === Useful http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html[Libtool] flags that can be put int tgt_LDFLAGS for a LTLIBRARY: * -export-dynamic * -export-symbols symfile * -export-symbols-regex regex * -module See libtool http://www.gnu.org/software/libtool/manual/html_node/Versioning.html["Versioning"] chapter about those: * -avoid-version * -version-info current[:revision[:age]] * -version-number major[:minor[:revision]] * -release major[:minor[:revision]] == Top-level pseudo-targets == === all === The default target when no other target is given on command-line. Builds all target files. ==== Simple targets ==== These are simple - either the file already exists, or the user needs to give build command. ==== Object-based targets ==== The targets in primaries PROGRAMS, LIBRARIES and LTLIBRARIES consist of multiple source files that need to be compiled into objects. Then the objects need to be linked into final target. The process is roughly following: . Dependencies are built (_LDADD, _LIBADD, _DEPENDENCIES). . Source list is filtered for extensions that can be compiled into object files, object file list is created based on them. The rest of files are used and dependencies for target, but otherwise ignored. . Object files are built. . Linker is picked based on source files - as there can be files in multiple languages, the most advanced language wins (the one that appears later in `AM_LANGUAGES`) . Final executable is linked. === install === Install all targets to their destination directories, which is mentioned in their target list variable name. Eg. `bin_PROGRAMS` will be installed to `$(bindir)`. If destination is named `noinst`, it will not be installed. If the flag `nobase` is given, the relative filename is kept, otherwise basename is taken and it will appear directly under destination directory. .Example: ------ include_HEADERS = func1.h lib/func2.h # Files will end up in: # $(includedir)/func1.h # $(includedir)/func2.h nobase_include_HEADERS = func1.h lib/func2.h # Files will end up in: # $(includedir)/func1.h # $(includedir)/lib/func2.h ------ === clean === - Remove files in `$(CLEANFILES)` - Remove built objects. - Remove target files, unless they are marked as `dist`. (Note: `HEADERS` primary is `dist` by default, all other are `nodist`) === distclean === - Remove files in `$(DISTCLEANFILES)` - Remove sources tagged with `nodist`. All sources as `dist` by default. === maintainer-clean === - Remove files in `$(MAINTAINERCLEANFILES)` === help === Describe top-level targets. === am-test === Regression test for low-level Antimake functions. === am-debug === Show Antimake internal state. == FEATURES == Done: - Big primaries: PROGRAMS, LIBRARIES, LTLIBRARIES - Small primaries: DATA, SCRIPTS, MANS, HEADERS - Flags: base nobase dist nodist noinst EXTRA - Target vars: SOURCES, CPPFLAGS, CFLAGS, LDFLAGS, LDADD/LIBADD - Separate build dir - Per-target objects - Languages: C, CXX - SUBDIRS, DIST_SUBDIRS - EMBED_SUBDIRS Todo: - Improve docs - Standardize and document how to extend - Deps with non-gcc? - Long if-s to support `O=` seems to break GNU Make 3.80. Drop `O=` or drop 3.80? Probably out of scope: - `make uninstall` - `make distcheck` - `make dist` from separate build dir - `install-(exec|data)-hook` - based on dir not primary - Default file list for `EXTRA_DIST`. (Problem: distclean / maintainer-clean) Definitely out of scope: - automake conditionals - automake extras (autoconf macros, ltdl) - automake nanny mode (gnu/gnits) == SEE ALSO == GNU Make Reference: http://www.gnu.org/software/make/manual/make.html#Quick-Reference[] Recursive Make Considered Harmful: http://miller.emu.id.au/pmiller/books/rmch/[] Paul's Rules of Makefiles: http://make.mad-scientist.us/rules.html[] Small BSD-ish build system: https://webkeks.org/hg/buildsys/[] GNU Make Standard Library: http://sourceforge.net/projects/gmsl/[] pgqd/lib/mk/Makefile.am0000664000401600040160000000035313175113172013266 0ustar cbecbe pkgconfig_DATA = libusual.pc dist_pkgdata_DATA = amext-libusual.mk amext-modes.mk amext-msvc.mk amext-cxx.mk dist_pkgdata_SCRIPTS = antimake.mk std-autogen.sh DISTCLEANFILES = libusual.pc EXTRA_DIST = antimake.txt safe-headers.sed pgqd/lib/mk/std-autogen.sh0000775000401600040160000000451013175113172014022 0ustar cbecbe#! /bin/sh # autogen for non-automake trees # # - it installs files: config.sub, config.guess, install-sh # - it installs ltmain.sh, if LT_INIT or *LIBTOOL macro is used # set -e USUAL_DIR="$1" test -n "${USUAL_DIR}" || USUAL_DIR="." test -f "${USUAL_DIR}/m4/usual.m4" || { echo usage: $0 USUAL_DIR exit 1 } # default programs ACLOCAL=${ACLOCAL:-aclocal} AUTOCONF=${AUTOCONF:-autoconf} AUTOHEADER=${AUTOHEADER:-autoheader} # If neither ACLOCAL/AUTOCONF/AUTOHEADER and # AUTOCONF_VERSION/AUTOMAKE_VERSION are configured, # pick any modern version to avoid pointless errors. if test "$AUTOCONF_VERSION" = ""; then if test "$AUTOCONF" = "autoconf"; then for ac in 70 69 68 67 66 65 64 63 62 61 60 59; do ac="2.$ac" if which autoconf-$ac > /dev/null 2>&1; then AUTOCONF_VERSION="$ac" echo "Using autoconf: $AUTOCONF_VERSION" break fi if which autoconf$ac > /dev/null 2>&1; then AUTOCONF_VERSION="$ac" echo "Using autoconf: $AUTOCONF_VERSION" break fi done fi fi if test "$AUTOMAKE_VERSION" = ""; then if test "$ACLOCAL" = "aclocal"; then for am in 1.16 1.15 1.14 1.13 1.12 1.11 1.10 1.9; do if which aclocal-$am > /dev/null 2>&1; then AUTOMAKE_VERSION="$am" echo "Using aclocal: $AUTOMAKE_VERSION" break fi if which aclocal$am > /dev/null 2>&1; then AUTOMAKE_VERSION="$am" echo "Using aclocal: $AUTOMAKE_VERSION" break fi done fi fi export AUTOCONF_VERSION AUTOMAKE_VERSION # detect first glibtoolize then libtoolize if test "x$LIBTOOLIZE" = "x"; then LIBTOOLIZE=glibtoolize which $LIBTOOLIZE >/dev/null 2>&1 \ || LIBTOOLIZE=libtoolize fi # # Workarounds for libtoolize randomness - it does not update # the files if they exist, except it requires install-sh. # rm -f config.guess config.sub install-sh ltmain.sh libtool cp -p ${USUAL_DIR}/mk/install-sh . if ${LIBTOOLIZE} --help | grep "[-][-]install" > /dev/null; then ${LIBTOOLIZE} -i -f -q -c else ${LIBTOOLIZE} -c fi # drop ltmain.sh if libtool is not used grep -E 'LT_INIT|LIBTOOL' configure.ac > /dev/null \ || rm -f ltmain.sh # Now generate configure & config.h ${ACLOCAL} -I ${USUAL_DIR}/m4 grep AC_CONFIG_HEADER configure.ac > /dev/null \ && ${AUTOHEADER} ${AUTOCONF} # clean junk rm -rf autom4te.* aclocal* pgqd/lib/mk/libusual.pc.in0000664000401600040160000000045413175113172014005 0ustar cbecbeprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ datarootdir=@datarootdir@ pkgdatadir=@pkgdatadir@ antimake=@pkgdatadir@/antimake.mk Name: libusual Description: Usual utility library for C Version: @PACKAGE_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lusual pgqd/lib/mk/install-sh0000775000401600040160000003253713175113172013247 0ustar cbecbe#!/bin/sh # install - install a program, script, or datafile scriptversion=2009-04-28.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: pgqd/lib/mk/amext-msvc.mk0000664000401600040160000000211113175113172013641 0ustar cbecbe# # Support for MSVC toolchain. # # Usage: # 1. Install coreutils (printf, tail) and make from gnuwin32. # 2. Make sure VC env variables are loaded (PATH) # SHELL = cmd.exe ShellQuote = "$(subst $$, \$$, $(subst ",\",$(subst \,\\,$(1))))" EXEEXT = .exe LIBEXT = .lib OBJEXT = .obj CC = cl -nologo CFLAGS = -O2 $(WFLAGS) WFLAGS = -W2 -w24013 CPP = $(CC) -E LDFLAGS = LIBS = -lws2_32 -ladvapi32 AR = lib ARFLAGS = -nologo LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Fe$(call vcFixPath,$@) Printf = printf $(subst %,%%,$(1)) $(2) MKDIR_P = md MkDir = if not exist $(call vcFixPath,$(1)) $(MKDIR_P) $(call vcFixPath,$(1)) vcFixPath = $(subst /,\,$(1)) vcFixLibs = $(patsubst %.a,%.lib,$(patsubst -l%,%.lib,$(1))) vcFixAll = $(call vcFixPath,$(call vcFixLibs,$(1))) define AM_LANG_C_COMPILE $(E) "CC" $< $(Q) $(COMPILE) -c -Fo$(call vcFixPath,$@) $< | tail -n+2 endef define AM_LANG_C_LINK $(E) "CCLD" $@ $(Q) $(LINK) $(call vcFixAll,$^ $(AM_LIBS) $(LIBS)) $(AM_LT_RPATH) endef define ar_lib $(E) "LIB" $@ $(Q) $(AR) $(ARFLAGS) -out:$(call vcFixPath,$@) $^ endef pgqd/lib/doc/0000775000401600040160000000000013175113172011367 5ustar cbecbepgqd/lib/doc/mainpage.dox0000664000401600040160000001447013175113172013672 0ustar cbecbe/** * @mainpage * * @section libusual libusual * * libusual is utility library. * * Unlike APR or GLIB which create their own API world, libusual * tries to use standardized API's whenever possible. * * Goals for portability APIs: * - Follow modern POSIX, BSD, glibc. Make the APIs available everywhere. * - If compat is impossible, allow the user code to compile - eg. UNIX * sockets on win32. * - Assume cooparating user: * - libusual needs to implement only API that are used and useful. No need try to provide full POSIX. * - user code survives gracefully when libusual provides less functionality. * * Goals for new APIs: * - Simple, clear API * - Simple, clear implementation. * - It is preferable to have simple code which can be copied and modified * for some special case than complex code that tries to handle * everything at once. * * @section antimake Antimake build system. * * Build system demos and docs. * * @section modules Module list. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Compat includes
Base C environment
ctype compat
Command line argument processing
Error handling for command-line tools
Async DNS lookup
Pthreads compat
Signal compat
Socket compat and helper functions
String compat and helper functions
Time compat and helper functions
fnmatch compat
wchar compat
Data Structures
Binary Tree
Crit-Bit Tree
Hash table
Binary heap
Double-linked list
Memory buffer
Minimal dict
Double-linked list for shared mem
List with stats
Refcounted strings
Data Processing
Bit arithmetic
Byte processing
Config parser
Endianess conversion
Misc arithmetic
Read/write JSON
Pseudo-random functions
POSIX regex compat
PostgreSQL data formats
Low-level UTF8 handling
Non-cryptographic hashing
CRC32
Jenkins' lookup3 hash
Siphash
In-memory randomized hashing
Jenkins' SpookyHash for 64-bit CPUs
Fast hash for 32-bit CPUs
Cryptography
Cryptographically Secure Randomness
Common API for cryptographic message digests
HMAC with digest
MD5 hash
SHA1 hash
SHA256/224 hashes
SHA512/384 hashes
SHA3/SHAKE hashes
Keccak sponge API
PRNG based on Keccak
Entropy collector
ChaCha cipher
Memory Allocation
Context Allocator framework
Extra allocators
Simple append-only memory pool
Slab allocator for same-size objects
Hierarchical allocator
OS support
libevent compat
Process daemonization
Various file I/O tools
Logging framework for daemons
Async Postgres connection framework
Safety wrappers around OS I/O
*/ pgqd/lib/doc/Doxyfile0000664000401600040160000017762413175113172013116 0ustar cbecbe# Doxyfile 1.6.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libusual # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 0.1 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = YES # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 0 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = usual doc # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.h *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = NO # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = NO # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = NO # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES pgqd/lib/doc/setup.dox0000664000401600040160000000006713175113172013246 0ustar cbecbe/** * @page setup How to setup libusual * * Foo */ pgqd/lib/test/0000775000401600040160000000000013175113172011601 5ustar cbecbepgqd/lib/test/test_bits.c0000664000401600040160000001310413175113172013744 0ustar cbecbe #include #include "test_common.h" /* * is_power_of_2 */ static void test_pow2(void *p) { int_check(is_power_of_2(0), 0); int_check(is_power_of_2(1), 1); int_check(is_power_of_2(2), 1); int_check(is_power_of_2(3), 0); end:; } /* * rol */ static void test_rol(void *p) { /* rol16 */ int_check(rol16(1, 1), 2); int_check(rol16(1, 15), 32768); int_check(rol16(0x8000, 1), 1); /* rol32 */ int_check(rol32(1, 1), 2); int_check(rol32(0x80000000, 1), 1); /* rol64 */ ull_check(rol64(1, 1), 2); ull_check(rol64(1, 63), 0x8000000000000000ULL); end:; } /* * ror */ static void test_ror(void *p) { /* ror16 */ int_check(ror16(1, 1), 0x8000); /* ror32 */ int_check(ror32(1, 1), 0x80000000); /* ror64 */ ull_check(ror64(1, 1), 0x8000000000000000ULL); end:; } /* * fls */ static void test_fls(void *p) { /* fls */ int_check(fls(0), 0); int_check(fls(1), 1); int_check(fls(3), 2); int_check(fls((int)-1), 32); /* flsl */ int_check(flsl(0), 0); int_check(flsl(1), 1); int_check(flsl(3), 2); if (sizeof(long) == 4) int_check(flsl((long)-1), 32); else int_check(flsl((long)-1), 64); /* flsll */ int_check(flsll(0), 0); int_check(flsll(1), 1); int_check(flsll(3), 2); int_check(flsll((long long)-1), 64); end:; } /* * ffs */ static void test_ffs(void *p) { /* ffs */ int_check(ffs(0), 0); int_check(ffs(1), 1); int_check(ffs(3), 1); int_check(ffs((int)-1), 1); int_check(ffs(ror32(1,1)), 32); /* flsl */ int_check(ffsl(0), 0); int_check(ffsl(1), 1); int_check(ffsl(3), 1); int_check(ffsl((long)-1), 1); if (sizeof(long) == 4) int_check(ffsl(ror32(1,1)), 32); else int_check(ffsl(ror64(1,1)), 64); /* ffsll */ int_check(ffsll(0), 0); int_check(ffsll(1), 1); int_check(ffsll(3), 1); int_check(ffsll((long long)-1), 1); ull_check((1ULL << 63), ror64(1,1)); int_check(ffsll(1ULL << 63), 64); int_check(ffsll(ror64(1,1)), 64); end:; } /* * safe mul */ static void test_safe_mul(void *p) { uint8_t v8; uint16_t v16; uint32_t v32; uint64_t v64; unsigned int i; unsigned long l; size_t s; tt_assert(safe_mul_uint8(&v8, 1, 1)); tt_assert(v8 == 1); tt_assert(safe_mul_uint8(&v8, 15, 15)); tt_assert(v8 == 15*15); tt_assert(!safe_mul_uint8(&v8, 16, 16)); tt_assert(v8 == 15 * 15); tt_assert(safe_mul_uint8(&v8, 255, 1)); tt_assert(v8 == 255); v8 = 0; tt_assert(safe_mul_uint8(&v8, 1, 255)); tt_assert(v8 == 255); tt_assert(safe_mul_uint8(&v8, 256/4, 3)); tt_assert(v8 == 3*256/4); v8 = 0; tt_assert(safe_mul_uint8(&v8, 3, 256/4)); tt_assert(v8 == 3*256/4); tt_assert(!safe_mul_uint8(&v8, 256/4, 5)); tt_assert(!safe_mul_uint8(&v8, 5, 256/4)); tt_assert(safe_mul_uint8(&v8, 0, 255)); tt_assert(v8 == 0); tt_assert(safe_mul_uint16(&v16, 1, 1)); tt_assert(v16 == 1); tt_assert(safe_mul_uint16(&v16, UINT8_MAX, UINT8_MAX)); tt_assert(v16 == (1U * UINT8_MAX * UINT8_MAX)); tt_assert(!safe_mul_uint16(&v16, UINT8_MAX+1, UINT8_MAX+1)); tt_assert(v16 == (1U * UINT8_MAX * UINT8_MAX)); tt_assert(safe_mul_uint16(&v16, UINT16_MAX, 1)); tt_assert(v16 == UINT16_MAX); v16 = 0; tt_assert(safe_mul_uint16(&v16, 1, UINT16_MAX)); tt_assert(v16 == UINT16_MAX); tt_assert(safe_mul_uint16(&v16, (1<<16)/4, 3)); tt_assert(v16 == 3U * (1<<16)/4); v16 = 0; tt_assert(safe_mul_uint16(&v16, 3, (1<<16)/4)); tt_assert(v16 == 3U * (1<<16)/4); tt_assert(!safe_mul_uint16(&v16, (1<<16)/4, 5)); tt_assert(!safe_mul_uint16(&v16, 5, (1<<16)/4)); tt_assert(safe_mul_uint16(&v16, UINT16_MAX, 0)); tt_assert(v16 == 0); tt_assert(safe_mul_uint32(&v32, 1, 1)); tt_assert(v32 == 1); tt_assert(safe_mul_uint32(&v32, UINT16_MAX, UINT16_MAX)); tt_assert(v32 == (1U * UINT16_MAX * UINT16_MAX)); tt_assert(!safe_mul_uint32(&v32, UINT16_MAX+1, UINT16_MAX+1)); tt_assert(v32 == (1U * UINT16_MAX * UINT16_MAX)); tt_assert(safe_mul_uint32(&v32, UINT32_MAX, 1)); tt_assert(v32 == UINT32_MAX); v32 = 0; tt_assert(safe_mul_uint32(&v32, 1, UINT32_MAX)); tt_assert(v32 == UINT32_MAX); tt_assert(safe_mul_uint32(&v32, (1ULL<<32)/4, 3)); tt_assert(v32 == 3 * (1ULL<<32)/4); v32 = 0; tt_assert(safe_mul_uint32(&v32, 3, (1ULL<<32)/4)); tt_assert(v32 == 3 * (1ULL<<32)/4); tt_assert(!safe_mul_uint32(&v32, (1ULL<<32)/4, 5)); tt_assert(!safe_mul_uint32(&v32, 5, (1ULL<<32)/4)); tt_assert(safe_mul_uint32(&v32, 0, UINT32_MAX)); tt_assert(v32 == 0); tt_assert(safe_mul_uint64(&v64, 1, 1)); tt_assert(v64 == 1); tt_assert(safe_mul_uint64(&v64, UINT32_MAX, UINT32_MAX)); tt_assert(v64 == (1ULL*UINT32_MAX*UINT32_MAX)); tt_assert(!safe_mul_uint64(&v64, UINT32_MAX+1ULL, UINT32_MAX+1ULL)); tt_assert(v64 == (1ULL*UINT32_MAX*UINT32_MAX)); tt_assert(safe_mul_uint64(&v64, UINT64_MAX, 1)); tt_assert(v64 == UINT64_MAX); v64 = 0; tt_assert(safe_mul_uint64(&v64, 1, UINT64_MAX)); tt_assert(v64 == UINT64_MAX); tt_assert(safe_mul_uint64(&v64, (1ULL<<(64-2)), 3)); tt_assert(v64 == (1ULL<<(64-2)) * 3); v64 = 0; tt_assert(safe_mul_uint64(&v64, 3, (1ULL<<(64-2)))); tt_assert(v64 == (1ULL<<(64-2)) * 3); tt_assert(!safe_mul_uint64(&v64, (1ULL<<(64-2)), 5)); tt_assert(!safe_mul_uint64(&v64, 5, (1ULL<<(64-2)))); tt_assert(safe_mul_uint64(&v64, UINT64_MAX, 0)); tt_assert(v64 == 0); tt_assert(safe_mul_uint(&i, UINT16_MAX, UINT16_MAX)); tt_assert(i == (1U * UINT16_MAX * UINT16_MAX)); tt_assert(safe_mul_ulong(&l, ULONG_MAX, 1)); tt_assert(l == ULONG_MAX); tt_assert(safe_mul_size(&s, SIZE_MAX, 1)); tt_assert(s == SIZE_MAX); tt_assert(safe_mul_size(&s, 1, SIZE_MAX)); tt_assert(s == SIZE_MAX); end:; } /* * Describe */ struct testcase_t bits_tests[] = { { "is_power_of_2", test_pow2 }, { "rol", test_rol }, { "ror", test_ror }, { "ffs", test_ffs }, { "fls", test_fls }, { "safe_mul", test_safe_mul }, END_OF_TESTCASES }; pgqd/lib/test/test_netdb.c0000664000401600040160000000161613175113172014104 0ustar cbecbe #include #include #include #include #include "test_common.h" static int gotres; static void cb_func(union sigval v) { gotres++; } static void test_gai(void *p) { int res; struct sigevent sev; struct gaicb req; struct gaicb *rlist[] = { &req }; memset(&req, 0, sizeof(req)); req.ar_name = "localhost"; memset(&sev, 0, sizeof(sev)); sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = cb_func; res = getaddrinfo_a(GAI_NOWAIT, rlist, 1, &sev); if (res == EAI_SYSTEM && errno == ENOSYS) { /* ok - no impl */ goto end; } else { int_check(res, 0); } while (gai_error(&req) == EAI_INPROGRESS || gotres == 0) usleep(10000); int_check(gai_error(&req), 0); freeaddrinfo(req.ar_result); int_check(gotres, 1); end:; } struct testcase_t netdb_tests[] = { { "getaddrinfo_a", test_gai }, END_OF_TESTCASES }; pgqd/lib/test/test_base.c0000664000401600040160000000331313175113172013716 0ustar cbecbe #include #include "test_common.h" #include struct somestruct { char a, b, c; }; static void test_ptr(void *p) { /* offsetof */ int_check(offsetof(struct somestruct, a), 0); int_check(offsetof(struct somestruct, b), 1); int_check(offsetof(struct somestruct, c), 2); /* container_of */ { struct somestruct s = {'a', 'b', 'c'}; char *pa = &s.a; char *pb = &s.b; char *pc = &s.c; struct somestruct *sa, *sb, *sc; sa = container_of(pa, struct somestruct, a); sb = container_of(pb, struct somestruct, b); sc = container_of(pc, struct somestruct, c); int_check(sa->a, 'a'); int_check(sb->b, 'b'); int_check(sc->c, 'c'); } /* alignof */ int_check(alignof(char), 1); int_check(alignof(short), 2); int_check(alignof(int), 4); /* CUSTOM_ALIGN */ int_check(CUSTOM_ALIGN(1, 4), 4); int_check(CUSTOM_ALIGN(2, 4), 4); int_check(CUSTOM_ALIGN(3, 4), 4); int_check(CUSTOM_ALIGN(4, 4), 4); int_check(CUSTOM_ALIGN(5, 4), 8); end:; } #ifdef _PACKED struct packed { char a; int b; char c; short d; } _PACKED; #endif static void test_misc(void *_p) { int i_4[4]; int i_2[2]; short s_4[4]; short s_2[2]; int_check(ARRAY_NELEM(i_4), 4); int_check(ARRAY_NELEM(i_2), 2); int_check(ARRAY_NELEM(s_4), 4); int_check(ARRAY_NELEM(s_2), 2); int_check(strcmp(__func__, "test_misc"), 0); #ifdef _PACKED int_check(sizeof(struct packed), 8); #endif end:; } static void test_reallocarray(void *_p) { void *p; p = reallocarray(NULL, 1, 1); tt_assert(p); free(p); p = reallocarray(NULL, SIZE_MAX, SIZE_MAX); tt_assert(p == NULL); end:; } struct testcase_t base_tests[] = { { "ptr", test_ptr }, { "misc", test_misc }, { "reallocarray", test_reallocarray }, END_OF_TESTCASES }; pgqd/lib/test/test_cfparser.ini0000664000401600040160000000012413175113172015143 0ustar cbecbe[one] str1 = val1 int = 5 bool = 1 [two] str2 = val2 time1 = 1.5 time2 = 2.5 pgqd/lib/test/test_psrandom.c0000664000401600040160000000741513175113172014636 0ustar cbecbe #include #include #include #include "test_common.h" #define str_check(a, b) tt_str_op(a, ==, b) #define tt_stri_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \ (strcasecmp(_val1,_val2) op 0),"<%s>") #define stri_check(a, b) tt_stri_op(a, ==, b) static const char *mkhex(const uint8_t *src, int len) { static char buf[1024 + 1]; static const char hextbl[] = "0123456789abcdef"; int i; for (i = 0; i < len; i++) { buf[i*2] = hextbl[src[i] >> 4]; buf[i*2+1] = hextbl[src[i] & 15]; } buf[i*2] = 0; return buf; } /* * Test if seeding works fine. */ static void test_seed(void *ptr) { uint32_t val1; pseudo_random_seed(1, 1); val1 = pseudo_random(); pseudo_random_seed(2, 2); pseudo_random(); pseudo_random_seed(1, 1); int_check(pseudo_random(), val1); end: pseudo_random_seed(test_seed1, test_seed2); } /* * Check bytes and algo stability. */ static const char *run_bytes(size_t count) { uint8_t res[512]; if (count >= sizeof res) return "NOMEM"; memset(res, 0, sizeof res); pseudo_random_bytes(res, count); if (res[count]) return "OVERFLOW"; return mkhex(res, count); } static void test_bytes(void *ptr) { pseudo_random_seed(1, 1); str_check(run_bytes(0), ""); str_check(run_bytes(10), "604913fc779c86c8c2eb"); str_check(run_bytes(5), "5d3e8b9dc2"); str_check(run_bytes(4), "47e760b3"); str_check(run_bytes(3), "a09744"); str_check(run_bytes(2), "85d6"); str_check(run_bytes(1), "93"); end: pseudo_random_seed(test_seed1, test_seed2); } /* * Test random value extraction */ static void test_random(void *z) { uint8_t buf[8]; uint32_t v1, v2; pseudo_random_seed(0, 0); pseudo_random_bytes(buf, 8); v1 = le32dec(buf); v2 = le32dec(buf+4); pseudo_random_seed(0, 0); int_check(v1, pseudo_random()); int_check(v2, pseudo_random()); end: pseudo_random_seed(test_seed1, test_seed2); } /* * Check range limit. */ static bool run_range(uint32_t limit) { bool res = false; int i; for (i = 0; i < 100; i++) { uint32_t v = pseudo_random_range(limit); if (limit == 0) { int_check(v, 0); } else { tt_assert(v < limit); } } res = true; end: return res; } static void test_range(void *z) { if (!run_range(1)) goto end; if (!run_range(0)) goto end; if (!run_range(255)) goto end; end: pseudo_random_seed(test_seed1, test_seed2); } /* * Test if core algo is sane. */ // orig code by Sebastiano Vigna static uint64_t xs128plus_orig(uint64_t s[2]) { uint64_t s1 = s[ 0 ]; const uint64_t s0 = s[ 1 ]; s[ 0 ] = s0; s1 ^= s1 << 23; // a return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0; // b, c } static void test_core(void *z) { uint64_t i, s_orig[2], s_cur[2], s_1024[16], s_bak[16]; s_orig[0] = s_cur[0] = UINT64_C(0x123456789abcdef1); s_orig[1] = s_cur[1] = UINT64_C(0xfedcba9876543210); for (i = 0; i < 100; i++) { xs128plus_orig(s_orig); xorshift128plus(&s_cur[0], &s_cur[1]); tt_assert(s_orig[0] == s_cur[0]); tt_assert(s_orig[1] == s_cur[1]); } for (i = 0; i < 16; i++) s_1024[i] = xorshift128plus(&s_cur[0], &s_cur[1]); memcpy(s_bak, s_1024, sizeof s_bak); xorshift1024plus(s_1024, 0); tt_assert(s_1024[0] == s_bak[0]); tt_assert(s_1024[1] != s_bak[1]); tt_assert(s_1024[1] != s_bak[0]); tt_assert(s_1024[2] == s_bak[2]); tt_assert(s_1024[15] == s_bak[15]); memcpy(s_bak, s_1024, sizeof s_bak); xorshift1024plus(s_1024, 15); tt_assert(s_1024[15] == s_bak[15]); tt_assert(s_1024[0] != s_bak[0]); tt_assert(s_1024[0] != s_bak[1]); tt_assert(s_1024[0] != s_bak[15]); end: pseudo_random_seed(test_seed1, test_seed2); } /* * Launcher. */ struct testcase_t psrandom_tests[] = { { "core", test_core }, { "seed", test_seed }, { "bytes", test_bytes }, { "random", test_random }, { "range", test_range }, END_OF_TESTCASES }; pgqd/lib/test/test_regex.c0000664000401600040160000000550213175113172014120 0ustar cbecbe#include #include #include "test_common.h" #define NMATCH 20 /* * quick regex sanity check */ /* execute basic regex and return result as string */ static const char *b_rx(const char *regex, const char *str, int flags) { static char buf[512]; regex_t rx; regmatch_t matches[NMATCH]; unsigned int nmatch, i; int err; char *dst = buf, *bufend = buf + sizeof buf; memset(&rx, 0, sizeof(rx)); memset(matches, -1, sizeof(&matches)); /* compile */ err = regcomp(&rx, regex, flags); if (err) goto fail; nmatch = rx.re_nsub; /* match */ err = regexec(&rx, str, NMATCH, matches, 0); if (err) goto fail; /* format result */ for (i = 0; i < nmatch + 1; i++) { regmatch_t *m = &matches[i]; *dst++ = '('; if (m->rm_so >= 0) dst += snprintf(dst, bufend - dst, "%d", (int)m->rm_so); else *dst++ = '?'; *dst++ = ','; if (m->rm_eo >= 0) dst += snprintf(dst, bufend - dst, "%d", (int)m->rm_eo); else *dst++ = '?'; if (dst >= bufend) return "bufover"; *dst++ = ')'; } regfree(&rx); return buf; fail: /* format error */ regfree(&rx); switch (err) { case REG_NOMATCH: return "NOMATCH"; case REG_BADBR: return "BADBR"; case REG_BADPAT: return "BADPAT"; case REG_BADRPT: return "BADRPT"; case REG_EBRACE: return "EBRACE"; case REG_EBRACK: return "EBRACK"; case REG_ECOLLATE: return "ECOLLATE"; case REG_ECTYPE: return "ECTYPE"; #ifdef REG_EEND case REG_EEND: return "EEND"; #endif case REG_EESCAPE: return "EESCAPE"; case REG_EPAREN: return "EPAREN"; case REG_ERANGE: return "ERANGE"; #ifdef REG_ESIZE case REG_ESIZE: return "ESIZE"; #endif case REG_ESPACE: return "ESPACE"; case REG_ESUBREG: return "ESUBREG"; #ifdef REG_ENOSYS case REG_ENOSYS: return "ENOSYS"; #endif #ifdef REG_EMPTY case REG_EMPTY: return "EMPTY"; #endif default: return "UNKNOWN_ERROR"; } } /* execute extended regex and return result as string */ static const char *e_rx(const char *regex, const char *str, int flags) { return b_rx(regex, str, flags | REG_EXTENDED); } static void test_regex(void *ptr) { str_check(e_rx("foo*", "foobar", 0), "(0,3)"); str_check(e_rx("foo(x)?.*", "foobar", 0), "(0,6)(?,?)"); str_check(e_rx("foo", "bar", 0), "NOMATCH"); str_check(e_rx("foo{5,1}", "bar", 0), "BADBR"); /* str_check(e_rx("(|)", "bar", 0), "BADPAT"); */ str_check(e_rx("*", "bar", 0), "BADRPT"); str_check(e_rx("foo{", "bar", 0), "EBRACE"); str_check(e_rx("fo[o", "bar", 0), "EBRACK"); str_check(e_rx("[[:foo:]]", "bar", 0), "ECTYPE"); str_check(e_rx("foo\\", "foobar", 0), "EESCAPE"); str_check(e_rx("fo(o", "bar", 0), "EPAREN"); str_check(e_rx("[a-b-c]", "bar", 0), "ERANGE"); str_check(b_rx("(\\1)", "bar", 0), "ESUBREG"); str_check(e_rx("[[:random:]]", "bar", 0), "ECTYPE"); end:; } /* * Describe */ struct testcase_t regex_tests[] = { { "minimal", test_regex }, END_OF_TESTCASES }; pgqd/lib/test/test_hashing.c0000664000401600040160000000347113175113172014432 0ustar cbecbe#include #include #include #include "test_common.h" static uint32_t xcrc32(const char *s) { return calc_crc32(s, strlen(s), 0); } static uint32_t xlookup3(const char *s) { return hash_lookup3(s, strlen(s)); } static void test_crc32(void *p) { int_check(xcrc32(""), 0); int_check(xcrc32("a"), 3904355907); int_check(xcrc32("abc"), 891568578); int_check(xcrc32("message digest"), 538287487); int_check(xcrc32("abcdefghijklmnopqrstuvwxyz"), 1277644989); int_check(xcrc32("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 532866770); int_check(xcrc32("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 2091469426); end:; } static void test_lookup3(void *p) { #ifdef WORDS_BIGENDIAN int_check(xlookup3(""), 3735928559); int_check(xlookup3("a"), -454251968); int_check(xlookup3("abc"), -1186250080); int_check(xlookup3("message digest"), 670730672); int_check(xlookup3("abcdefghijklmnopqrstuvwxyz"), 251682059); int_check(xlookup3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 567386262); int_check(xlookup3("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 312582506); #else int_check(xlookup3(""), 3735928559); int_check(xlookup3("a"), 1490454280); int_check(xlookup3("abc"), 238646833); int_check(xlookup3("message digest"), 2512672053); int_check(xlookup3("abcdefghijklmnopqrstuvwxyz"), 1966650813); int_check(xlookup3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 3992286962); int_check(xlookup3("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 2776963519); #endif end:; } struct testcase_t hashing_tests[] = { { "crc32", test_crc32 }, { "lookup3", test_lookup3 }, END_OF_TESTCASES }; pgqd/lib/test/test_shlist.c0000664000401600040160000000655613175113172014326 0ustar cbecbe#include #include "test_common.h" #include struct MyNode { struct SHList node; char val[16]; }; static const char *xval(const struct SHList *elem) { const struct MyNode *n; if (!elem) return NULL; n = container_of(elem, struct MyNode, node); return n->val; } static struct MyNode *new_node(int v) { struct MyNode *n = malloc(sizeof(*n)); if (!n) return NULL; shlist_init(&n->node); snprintf(n->val, sizeof(n->val), "%d", v); return n; } static const char *check_list(struct SHList *list) { struct SHList *old, *cur; old = NULL; for (cur = shlist_get_next(list); cur != list; cur = shlist_get_next(cur)) { if (old) { if (shlist_get_prev(cur) != old) return "FAIL 1"; } else { if (shlist_get_prev(cur) != list) return "FAIL 2"; } old = cur; } if (shlist_get_prev(list) != ((old) ? old : list)) return "FAIL 3"; return "OK"; } static const char *xshow(struct SHList *list) { static char res[1024]; struct SHList *el; const char *ck = check_list(list); if (strcmp(ck, "OK") != 0) return ck; res[0] = 0; shlist_for_each(el, list) { if (res[0]) strlcat(res, ",", sizeof(res)); strlcat(res, xval(el), sizeof(res)); } return res; } static const char *xadd(struct SHList *list, int v) { struct MyNode *n = new_node(v); if (!n) return "FAIL"; shlist_append(list, &n->node); return xshow(list); } static const char *xadd1(struct SHList *list, int v) { struct MyNode *n = new_node(v); if (!n) return "FAIL"; shlist_prepend(list, &n->node); return xshow(list); } static const char *xdel(struct SHList *list, int v) { char buf[32]; struct SHList *el, *tmp; struct MyNode *n; snprintf(buf, sizeof(buf), "%d", v); shlist_for_each_safe(el, list, tmp) { n = container_of(el, struct MyNode, node); if (strcmp(buf, n->val) == 0) { shlist_remove(el); free(n); } } if (!check_list(list)) return "FAIL"; return xshow(list); } static void test_shlist(void *p) { struct SHList rlist, *list = &rlist; shlist_init(list); str_check(check_list(list), "OK"); str_check(xadd(list, 2), "2"); str_check(xadd1(list, 1), "1,2"); str_check(xadd(list, 3), "1,2,3"); str_check(xadd(list, 4), "1,2,3,4"); str_check(check_list(list), "OK"); { struct MyNode *n; struct SHList *el; str_check(xadd1(list, 0), "0,1,2,3,4"); el = shlist_pop(list); n = container_of(el, struct MyNode, node); str_check(n->val, "0"); free(n); } { struct MyNode *n; str_check(xadd1(list, 0), "0,1,2,3,4"); n = shlist_pop_type(list, struct MyNode, node); str_check(n->val, "0"); free(n); } str_check(xval(shlist_first(list)), "1"); str_check(xval(shlist_last(list)), "4"); int_check(shlist_empty(list), 0); str_check(xdel(list, 2), "1,3,4"); str_check(xdel(list, 1), "3,4"); str_check(xdel(list, 4), "3"); str_check(xdel(list, 3), ""); str_check(check_list(list), "OK"); int_check(shlist_empty(list), 1); end:; } static void test_shlist_remove(void *p) { static struct SHList xlist, xnode; struct SHList *list = &xlist; struct SHList *node = &xnode; shlist_init(list); tt_assert(shlist_empty(list)); shlist_append(list, node); tt_assert(!shlist_empty(list)); tt_assert(!shlist_empty(list)); shlist_remove(node); tt_assert(shlist_empty(node)); tt_assert(shlist_empty(list)); end:; } struct testcase_t shlist_tests[] = { { "remove", test_shlist_remove }, { "basic", test_shlist }, END_OF_TESTCASES }; pgqd/lib/test/test_aatree.c0000664000401600040160000001057513175113172014255 0ustar cbecbe #include #include #include #include #include "test_common.h" static char *OK = "OK"; typedef struct MyNode MyNode; struct MyNode { struct AANode node; int value; }; static int my_node_pair_cmp(const struct AANode *n1, const struct AANode *n2) { const struct MyNode *m1 = container_of(n1, struct MyNode, node); const struct MyNode *m2 = container_of(n2, struct MyNode, node); return m1->value - m2->value; } static int my_node_cmp(uintptr_t value, struct AANode *node) { MyNode *my = container_of(node, MyNode, node); return value - my->value; } static MyNode *make_node(int value) { MyNode *node = malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); node->value = value; return node; } static void my_node_free(struct AANode *node, void *arg) { MyNode *my = container_of(node, MyNode, node); free(my); } /* * Test tree sanity */ static const char *mkerr(const char *msg, int v, const struct AANode *node) { static char buf[128]; snprintf(buf, sizeof(buf), "%s: %d", msg, v); return buf; } static const char *check_sub(const struct AATree *tree, const struct AANode *node, int i) { int cmp_left = 0, cmp_right = 0; const char *res; if (aatree_is_nil_node(node)) return OK; if (node->level != node->left->level + 1) return mkerr("bad left level", i, node); if (!((node->level == node->right->level + 1) || (node->level == node->right->level && node->right->level != node->right->level + 1))) return mkerr("bad right level", i, node); if (!aatree_is_nil_node(node->left)) cmp_left = my_node_pair_cmp(node, node->left); if (!aatree_is_nil_node(node->right)) cmp_right = my_node_pair_cmp(node, node->right); if (cmp_left < 0) return mkerr("wrong left order", i, node); if (cmp_right > 0) return mkerr("wrong right order", i, node); res = check_sub(tree, node->left, i); if (!res) res = check_sub(tree, node->right, i); return res; } static const char *check(struct AATree *tree, int i) { return check_sub(tree, tree->root, i); } /* * checking operations */ static const char * my_search(struct AATree *tree, int value) { struct AANode *res; res = aatree_search(tree, value); return res ? OK : "not found"; } static const char *my_insert(struct AATree *tree, int value) { MyNode *my = make_node(value); aatree_insert(tree, value, &my->node); return check(tree, value); } static const char *my_remove(struct AATree *tree, int value) { const char *res; res = my_search(tree, value); if (res != OK) return res; aatree_remove(tree, value); res = check(tree, value); if (res != OK) return res; if (aatree_search(tree, value) != NULL) return "still found"; return OK; } /* * Simple opeartions. */ static void test_aatree_basic(void *p) { struct AATree tree[1]; int i; aatree_init(tree, my_node_cmp, my_node_free); str_check(my_search(tree, 1), "not found"); for (i = 0; i < 15; i++) { str_check(my_insert(tree, i), "OK"); } for (i = -1; i > -15; i--) { str_check(my_insert(tree, i), "OK"); } for (i = 30; i < 45; i++) { str_check(my_insert(tree, i), "OK"); } for (i = 15; i < 30; i++) { str_check(my_insert(tree, i), "OK"); } for (i = -14; i < 45; i++) { str_check(my_remove(tree, i), "OK"); } end: aatree_destroy(tree); } /* * randomized test */ #define RSIZE 3000 static int get_next(bool with_stat, bool added[]) { int r = pseudo_random_range(RSIZE); int i = r; while (1) { if (added[i] == with_stat) return i; if (++i >= RSIZE) i = 0; if (i == r) return -1; } } static void test_aatree_random(void *p) { bool is_added[RSIZE]; int prefer_remove = 0; /* 0 - insert, 1 - delete */ int n; int op; /* 0 - insert, 1 - delete */ struct AATree tree[1]; unsigned long long total = 0; memset(is_added, 0, sizeof(is_added)); aatree_init(tree, my_node_cmp, my_node_free); while (total < 20000) { int r = pseudo_random_range(16); if (prefer_remove) op = r > 5; else op = r > 10; /* op = 0; */ n = get_next(op, is_added); if (n < 0) { if (prefer_remove == op) { prefer_remove = !prefer_remove; } continue; } if (op == 0) { str_check(my_insert(tree, n), "OK"); is_added[n] = 1; } else { str_check(my_remove(tree, n), "OK"); is_added[n] = 0; } total++; } end: aatree_destroy(tree); } struct testcase_t aatree_tests[] = { { "basic", test_aatree_basic }, { "random", test_aatree_random }, END_OF_TESTCASES }; pgqd/lib/test/test_strpool.c0000664000401600040160000000217413175113172014512 0ustar cbecbe #include #include "test_common.h" #include static void test_strpool(void *p) { struct StrPool *pool; struct PStr *s; pool = strpool_create(NULL); tt_assert(pool); strpool_free(pool); pool = strpool_create(NULL); tt_assert(pool); int_check(strpool_total(pool), 0); s = strpool_get(pool, "foo", -1); str_check(s->str, "foo"); int_check(s->refcnt, 1); int_check(s->len, 3); int_check(strpool_total(pool), 1); tt_assert(s == strpool_get(pool, "fooTAIL", 3)); int_check(s->refcnt, 2); int_check(strpool_total(pool), 1); strpool_incref(s); int_check(s->refcnt, 3); strpool_decref(s); int_check(s->refcnt, 2); strpool_decref(s); int_check(s->refcnt, 1); int_check(strpool_total(pool), 1); strpool_decref(s); int_check(strpool_total(pool), 0); strpool_free(pool); /* free strc with strings */ pool = strpool_create(NULL); tt_assert(pool); s = strpool_get(pool, "foo", -1); s = strpool_get(pool, "bar", 3); int_check(strpool_total(pool), 2); strpool_free(pool); end:; } /* * Describe */ struct testcase_t strpool_tests[] = { { "strpool", test_strpool }, END_OF_TESTCASES }; pgqd/lib/test/test_utf8.c0000664000401600040160000001257013175113172013677 0ustar cbecbe #include #include "test_common.h" #include static int uget1(int a) { char buf[2] = { a, 0 }; const char *p = buf; return utf8_get_char(&p, buf + 1); } static int uget2(int a, int b) { char buf[3] = { a, b, 0 }; const char *p = buf; return utf8_get_char(&p, buf + 2); } static int uget3(int a, int b, int c) { char buf[4] = { a, b, c, 0 }; const char *p = buf; return utf8_get_char(&p, buf + 3); } static int uget4(int a, int b, int c, int d) { char buf[5] = { a, b, c, d, 0 }; const char *p = buf; return utf8_get_char(&p, buf + 4); } static const char *mkseq(uint32_t c, int n) { static char buf[8]; static const uint8_t prefix[] = { 0, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; int i; for (i = n - 1; i > 0; i--) { buf[i] = (c & 0x3F) | 0x80; c >>= 6; } buf[0] = prefix[n-1] | c; return buf; } static int readseq(uint32_t c, int n) { const char *p = mkseq(c, n); return utf8_get_char(&p, p + n); } static void test_utf8_char_size(void *p) { int_check(utf8_char_size(0), 1); int_check(utf8_char_size('a'), 1); int_check(utf8_char_size(0x7F), 1); int_check(utf8_char_size(0x80), 2); int_check(utf8_char_size(0x7FF), 2); int_check(utf8_char_size(0x800), 3); int_check(utf8_char_size(0xFFFF), 3); int_check(utf8_char_size(0x10000), 4); int_check(utf8_char_size(0x100000), 4); int_check(utf8_char_size(0x10FFFF), 4); end:; } static void test_utf8_seq_size(void *p) { int_check(utf8_seq_size(0), 1); int_check(utf8_seq_size(0x7F), 1); int_check(utf8_seq_size(0x80), 0); int_check(utf8_seq_size(0xBF), 0); int_check(utf8_seq_size(0xC0), 0); int_check(utf8_seq_size(0xC1), 0); int_check(utf8_seq_size(0xC2), 2); int_check(utf8_seq_size(0xDF), 2); int_check(utf8_seq_size(0xE0), 3); int_check(utf8_seq_size(0xEF), 3); int_check(utf8_seq_size(0xF0), 4); int_check(utf8_seq_size(0xF4), 4); int_check(utf8_seq_size(0xF5), 0); int_check(utf8_seq_size(0xFF), 0); end:; } static void test_utf8_get_char(void *p) { int_check(uget1(0), 0); int_check(uget1(0x7F), 0x7F); int_check(uget2(0xC2, 0xA2), 0xA2); int_check(uget2(0xC2, 0xA2), 0xA2); int_check(uget3(0xE2, 0x82, 0xAC), 0x20AC); int_check(uget4(0xF0, 0xA4, 0xAD, 0xA2), 0x024B62); /* invalid reads */ int_check(uget1(0x80), -0x80); int_check(uget1(0xC1), -0xC1); int_check(uget3(0xE0, 0x82, 0xAC), -0xE0); /* short reads */ int_check(uget1(0xC2), -0xC2); int_check(uget2(0xE2, 0x82), -0xE2); int_check(uget3(0xF0, 0xA4, 0xAD), -0xF0); /* good boundaries */ int_check(readseq(0x7f, 1), 0x7f); int_check(readseq(0x80, 2), 0x80); int_check(readseq(0x7ff, 2), 0x7ff); int_check(readseq(0x800, 3), 0x800); int_check(readseq(0xffff, 3), 0xffff); int_check(readseq(0x10000, 4), 0x10000); int_check(readseq(0x10ffff, 4), 0x10ffff); int_check(readseq(0xd7ff, 3), 0xd7ff); int_check(readseq(0xe000, 3), 0xe000); /* bad boundaries */ int_check(readseq(0x7f, 2), -193); int_check(readseq(0x7ff, 3), -224); int_check(readseq(0xffff, 4), -240); int_check(readseq(0x110000, 4), -244); int_check(readseq(0x10ffff, 5), -248); int_check(readseq(0xd800, 3), -237); int_check(readseq(0xdfff, 3), -237); end:; } static const char *uput(unsigned c, int buflen) { static char res[64]; unsigned char buf[8]; char *dst = (char *)buf; char *dstend = (char *)buf + buflen; unsigned len, i; bool ok; memset(buf, 11, sizeof(buf)); ok = utf8_put_char(c, &dst, dstend); if (!ok) return "FAILED"; len = dst - (char *)buf; for (i = len; i < 8; i++) { if (buf[i] != 11) return "OVER"; } snprintf(res, sizeof(res), "%02X %02X %02X %02X %02X %02X", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); if (len) res[len*3 - 1] = 0; else res[0] = 0; return res; } static void test_utf8_put_char(void *p) { str_check(uput(0, 1), "00"); str_check(uput(0x7F, 1), "7F"); str_check(uput(0xA2, 2), "C2 A2"); str_check(uput(0x20AC, 3), "E2 82 AC"); str_check(uput(0x024B62, 4), "F0 A4 AD A2"); str_check(uput(0x80FFFFFF, 5), ""); str_check(uput(0xD801, 5), ""); str_check(uput(0xFEFF, 5), "EF BB BF"); str_check(uput(0xFFFE, 5), "EF BF BE"); str_check(uput(0, 0), "FAILED"); str_check(uput(0xA2, 1), "FAILED"); str_check(uput(0x20AC, 2), "FAILED"); str_check(uput(0x20AC, 1), "FAILED"); str_check(uput(0x024B62, 3), "FAILED"); str_check(uput(0x024B62, 2), "FAILED"); str_check(uput(0x024B62, 1), "FAILED"); str_check(uput(0x024B62, 0), "FAILED"); end:; } static int validseq(uint32_t c, int n) { const char *p = mkseq(c, n); return utf8_validate_seq(p, p + n); } static void test_utf8_validate_seq(void *p) { /* good boundaries */ int_check(validseq(0x7f, 1), 1); int_check(validseq(0x80, 2), 2); int_check(validseq(0x7ff, 2), 2); int_check(validseq(0x800, 3), 3); int_check(validseq(0xffff, 3), 3); int_check(validseq(0x10000, 4), 4); int_check(validseq(0x10ffff, 4), 4); int_check(validseq(0xd7ff, 3), 3); int_check(validseq(0xe000, 3), 3); /* bad boundaries */ int_check(validseq(0x7f, 2), 0); int_check(validseq(0x7ff, 3), 0); int_check(validseq(0xffff, 4), 0); int_check(validseq(0x110000, 4), 0); int_check(validseq(0x10ffff, 5), 0); int_check(validseq(0xd800, 3), 0); int_check(validseq(0xdfff, 3), 0); end:; } /* * Describe */ struct testcase_t utf8_tests[] = { { "utf8_char_size", test_utf8_char_size }, { "utf8_seq_size", test_utf8_seq_size }, { "utf8_get_char", test_utf8_get_char }, { "utf8_put_char", test_utf8_put_char }, { "utf8_validate_seq", test_utf8_validate_seq }, END_OF_TESTCASES }; pgqd/lib/test/try_locales.sh0000775000401600040160000000126313175113172014462 0ustar cbecbe#! /bin/sh make -C .. && make all regtest_compat echo "" echo "# regtest_compat, no locale" ./regtest_compat --quiet echo "# regtest_system, no locale" ./regtest_system --quiet export USE_LOCALE=1 lclist="en_US.UTF-8 ru_RU.UTF-8 et_EE.UTF-8 fa_IR.UTF-8 ps_AF.UTF-8 aa_ER.UTF-8 ja_JP.UTF-8" lclist="$lclist et_EE.ISO-8859-1 ru_RU.koi-8r ja_JP.EUC-JP zh_CN.BIG5" for lc in $lclist; do if locale -a | grep -i "`echo $lc|sed 's/-//g'`" > /dev/null; then LC_ALL=$lc export LC_ALL echo "# regtest_compat, LC_ALL=$LC_ALL" ./regtest_compat --quiet echo "# regtest_system, LC_ALL=$LC_ALL" ./regtest_system --quiet else echo "### $lc not available ###" fi done pgqd/lib/test/test_endian.c0000664000401600040160000000452513175113172014250 0ustar cbecbe#include #include "test_common.h" #include /* * bswap*() */ static void test_bswap(void *p) { int_check(bswap16(0xff01), 0x01ff); int_check(bswap32(0x01020304), 0x04030201); ull_check(bswap64(0x0102030405060708ULL), 0x0807060504030201ULL); end:; } /* * *enc(), *dec() */ static uint64_t tdecode(int t, ...) { uint8_t buf[16]; bool be = t > 0; va_list ap; uint64_t val = 777; int i; if (t < 0) t = -t; va_start(ap, t); memset(buf, 0xC1, sizeof(buf)); for (i = 0; i < t; i++) buf[i] = va_arg(ap, int); va_end(ap); if (be) { switch (t) { case 2: val = be16dec(buf); break; case 4: val = be32dec(buf); break; case 8: val = be64dec(buf); break; } } else { switch (t) { case 2: val = le16dec(buf); break; case 4: val = le32dec(buf); break; case 8: val = le64dec(buf); break; } } return val; } static const char *tencode(int t, uint64_t val) { static char res[64]; uint8_t buf[16]; bool be = t > 0; int i; if (t < 0) t = -t; memset(buf, 0xFC, sizeof(buf)); if (be) { switch (t) { case 2: be16enc(buf, val); break; case 4: be32enc(buf, val); break; case 8: be64enc(buf, val); break; } } else { switch (t) { case 2: le16enc(buf, val); break; case 4: le32enc(buf, val); break; case 8: le64enc(buf, val); break; } } for (i = t; i < (int)sizeof(buf); i++) { if (buf[i] != 0xFC) return "OVER"; } snprintf(res, sizeof(res), "%02X %02X %02X %02X %02X %02X %02X %02X ", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); res[t*3 - 1] = 0; return res; } static void test_encdec(void *p) { ull_check(tdecode( 2, 1,2), 0x0102); ull_check(tdecode(-2, 1,2), 0x0201); ull_check(tdecode( 4, 1,2,3,4), 0x01020304); ull_check(tdecode(-4, 1,2,3,4), 0x04030201); ull_check(tdecode( 8, 1,2,3,4,5,6,7,8), 0x0102030405060708); ull_check(tdecode(-8, 1,2,3,4,5,6,7,8), 0x0807060504030201); str_check(tencode( 2, 0x0102), "01 02"); str_check(tencode(-2, 0x0102), "02 01"); str_check(tencode( 4, 0x01020304), "01 02 03 04"); str_check(tencode(-4, 0x01020304), "04 03 02 01"); str_check(tencode( 8, 0x0102030405060708ULL), "01 02 03 04 05 06 07 08"); str_check(tencode(-8, 0x0102030405060708ULL), "08 07 06 05 04 03 02 01"); end:; } /* * Describe */ struct testcase_t endian_tests[] = { { "bswap", test_bswap }, { "encdec", test_encdec }, END_OF_TESTCASES }; pgqd/lib/test/test_common.h0000664000401600040160000000356413175113172014311 0ustar cbecbe #include #include "tinytest.h" #include "tinytest_macros.h" #define str_check(a, b) tt_str_op(a, ==, b) #define int_check(a, b) tt_int_op(a, ==, b) #define ull_check(a, b) tt_assert_op_type(a, ==, b, uint64_t, "%" PRIu64) #define str_any2(val, a, b) \ do { \ const char *res = (val); \ if (strcmp(res, a) && strcmp(res, b)) \ str_check(res, a); \ } while (0) #define str_any3(val, a, b, c) \ do { \ const char *res = (val); \ if (strcmp(res, a) && strcmp(res, b) && strcmp(res, c)) \ str_check(res, a); \ } while (0) extern struct testcase_t aatree_tests[]; extern struct testcase_t base_tests[]; extern struct testcase_t bits_tests[]; extern struct testcase_t cbtree_tests[]; extern struct testcase_t cfparser_tests[]; extern struct testcase_t crypto_tests[]; extern struct testcase_t ctype_tests[]; extern struct testcase_t cxalloc_tests[]; extern struct testcase_t endian_tests[]; extern struct testcase_t event_tests[]; extern struct testcase_t fileutil_tests[]; extern struct testcase_t fnmatch_tests[]; extern struct testcase_t getopt_tests[]; extern struct testcase_t hashing_tests[]; extern struct testcase_t hashtab_tests[]; extern struct testcase_t heap_tests[]; extern struct testcase_t json_tests[]; extern struct testcase_t list_tests[]; extern struct testcase_t mdict_tests[]; extern struct testcase_t netdb_tests[]; extern struct testcase_t pgutil_tests[]; extern struct testcase_t psrandom_tests[]; extern struct testcase_t regex_tests[]; extern struct testcase_t shlist_tests[]; extern struct testcase_t socket_tests[]; extern struct testcase_t string_tests[]; extern struct testcase_t strpool_tests[]; extern struct testcase_t talloc_tests[]; extern struct testcase_t time_tests[]; extern struct testcase_t tls_tests[]; extern struct testcase_t utf8_tests[]; extern struct testcase_t wchar_tests[]; extern unsigned long long test_seed1, test_seed2; pgqd/lib/test/test_pgutil.c0000664000401600040160000001125113175113172014310 0ustar cbecbe #include #include "test_common.h" /* * pg_quote_literal */ static char *run_quote_lit(char *dst, const char *src, int size) { if (pg_quote_literal(dst, src, size)) return dst; return "FAIL"; } static void test_quote_lit(void *ptr) { char buf[128]; str_check(run_quote_lit(buf, "", 16), "''"); str_check(run_quote_lit(buf, "a", 16), "'a'"); str_check(run_quote_lit(buf, "a'a", 16), "'a''a'"); str_check(run_quote_lit(buf, "a\\a", 16), "E'a\\\\a'"); str_check(run_quote_lit(buf, "", 3), "''"); str_check(run_quote_lit(buf, "", 2), "FAIL"); str_check(run_quote_lit(buf, "", 1), "FAIL"); str_check(run_quote_lit(buf, "", 0), "FAIL"); str_check(run_quote_lit(buf, "a'a", 7), "'a''a'"); str_check(run_quote_lit(buf, "a'a", 6), "FAIL"); str_check(run_quote_lit(buf, "a\\a", 8), "E'a\\\\a'"); str_check(run_quote_lit(buf, "a\\a", 7), "FAIL"); str_check(run_quote_lit(buf, "a", 4), "'a'"); str_check(run_quote_lit(buf, "a", 3), "FAIL"); end:; } /* * quote_ident */ static char *qident(char *dst, const char *src, int size) { if (pg_quote_ident(dst, src, size)) return dst; return "FAIL"; } static void test_quote_ident(void *ptr) { char buf[128]; str_check(qident(buf, "", 16), "\"\""); str_check(qident(buf, "id_", 16), "id_"); str_check(qident(buf, "_id", 16), "_id"); str_check(qident(buf, "Baz", 16), "\"Baz\""); str_check(qident(buf, "baZ", 16), "\"baZ\""); str_check(qident(buf, "b z", 16), "\"b z\""); str_check(qident(buf, "5id", 16), "\"5id\""); str_check(qident(buf, "\"", 16), "\"\"\"\""); str_check(qident(buf, "a\"b", 16), "\"a\"\"b\""); str_check(qident(buf, "WHERE", 16), "\"WHERE\""); str_check(qident(buf, "where", 16), "\"where\""); str_check(qident(buf, "here", 16), "here"); str_check(qident(buf, "in", 16), "\"in\""); str_check(qident(buf, "", 3), "\"\""); str_check(qident(buf, "", 2), "FAIL"); str_check(qident(buf, "", 1), "FAIL"); str_check(qident(buf, "", 0), "FAIL"); str_check(qident(buf, "i", 2), "i"); str_check(qident(buf, "i", 1), "FAIL"); str_check(qident(buf, "i", 0), "FAIL"); str_check(qident(buf, "a\"b", 7), "\"a\"\"b\""); str_check(qident(buf, "a\"b", 6), "FAIL"); str_check(qident(buf, "a\"b", 5), "FAIL"); str_check(qident(buf, "a\"b", 4), "FAIL"); str_check(qident(buf, "a\"b", 3), "FAIL"); end:; } /* * quote_fqident */ static char *fqident(char *dst, const char *src, int size) { if (pg_quote_fqident(dst, src, size)) return dst; return "FAIL"; } static void test_quote_fqident(void *ptr) { char buf[128]; str_check(fqident(buf, "", 16), "public.\"\""); str_check(fqident(buf, "baz.foo", 16), "baz.foo"); str_check(fqident(buf, "baz.foo.bar", 16), "baz.\"foo.bar\""); str_check(fqident(buf, "where.in", 16), "\"where\".\"in\""); str_check(fqident(buf, "a.b", 4), "a.b"); str_check(fqident(buf, "a.b", 3), "FAIL"); str_check(fqident(buf, "a.b", 1), "FAIL"); str_check(fqident(buf, "a.b", 0), "FAIL"); str_check(fqident(buf, "i", 9), "public.i"); str_check(fqident(buf, "i", 8), "FAIL"); end:; } /* * pg_parse_array */ static char *aparse(const char *src) { struct StrList *sl = pg_parse_array(src, NULL); static char buf[1024]; char *dst = buf; const char *s; int len; bool first = true; if (!sl) return "FAIL"; while (!strlist_empty(sl)) { if (first) first = false; else *dst++ = ':'; s = strlist_pop(sl); if (!s) { memcpy(dst, "NULL", 5); dst += 4; } else { len = strlen(s); memcpy(dst, s, len); free(s); dst += len; } } *dst = 0; strlist_free(sl); return buf; } static void test_parse_array(void *ptr) { str_check(aparse("{a,b,c}"), "a:b:c"); str_check(aparse("{a}"), "a"); str_check(aparse("{}"), ""); str_check(aparse("{ a }"), "a"); str_check(aparse("{null}"), "NULL"); str_check(aparse("{ Null , NULL , nUlL }"), "NULL:NULL:NULL"); str_check(aparse("{ \"Null\" , \"NULL\" , \"nUlL\" }"), "Null:NULL:nUlL"); str_check(aparse("{ \"\",\"\",\"\" }"), "::"); str_check(aparse("{,}"), "FAIL"); str_check(aparse("{ a b c , d,e ,f}"), "a b c:d:e:f"); str_check(aparse("{ \" a b c \" , \",d,\"}"), " a b c :,d,"); str_check(aparse("[1,2]={7,8,9}"), "7:8:9"); str_check(aparse("[1,2.={7}"), "FAIL"); str_check(aparse("{ \\\" , \"\\\"\" }"), "\":\""); str_check(aparse("{ \\,,\\\\}"), ",:\\"); str_check(aparse("{\\}}"), "}"); str_check(aparse("{abc"), "FAIL"); str_check(aparse(""), "FAIL"); str_check(aparse("{\"abc}"), "FAIL"); str_check(aparse("{\\"), "FAIL"); str_check(aparse("{abc ,"), "FAIL"); end:; } /* * Describe */ struct testcase_t pgutil_tests[] = { { "pg_quote_literal", test_quote_lit }, { "pg_quote_ident", test_quote_ident }, { "pg_quote_fqident", test_quote_fqident }, { "pg_parse_array", test_parse_array }, END_OF_TESTCASES }; pgqd/lib/test/tinytest_demo.c0000664000401600040160000001572213175113172014643 0ustar cbecbe/* tinytest_demo.c -- Copyright 2009 Nick Mathewson * * 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. */ /* Welcome to the example file for tinytest! I'll show you how to set up * some simple and not-so-simple testcases. */ /* Make sure you include these headers. */ #include "tinytest.h" #include "tinytest_macros.h" #include #include #include #include /* ============================================================ */ /* First, let's see if strcmp is working. (All your test cases should be * functions declared to take a single void * as) an argument. */ void test_strcmp(void *data) { (void)data; /* This testcase takes no data. */ /* Let's make sure the empty string is equal to itself */ if (strcmp("","")) { /* This macro tells tinytest to stop the current test * and go straight to the "end" label. */ tt_abort_msg("The empty string was not equal to itself"); } /* Pretty often, calling tt_abort_msg to indicate failure is more heavy-weight than you want. Instead, just say: */ tt_assert(strcmp("testcase", "testcase") == 0); /* Occasionally, you don't want to stop the current testcase just because a single assertion has failed. In that case, use tt_want: */ tt_want(strcmp("tinytest", "testcase") > 0); /* You can use the tt_*_op family of macros to compare values and to fail unless they have the relationship you want. They produce more useful output than tt_assert, since they display the actual values of the failing things. Fail unless strcmp("abc, "abc") == 0 */ tt_int_op(strcmp("abc", "abc"), ==, 0); /* Fail unless strcmp("abc, "abcd") is less than 0 */ tt_int_op(strcmp("abc", "abcd"), < , 0); /* Incidentally, there's a test_str_op that uses strcmp internally. */ tt_str_op("abc", <, "abcd"); /* Every test-case function needs to finish with an "end:" label and (optionally) code to clean up local variables. */ end: ; } /* ============================================================ */ /* Now let's mess with setup and teardown functions! These are handy if you have a bunch of tests that all need a similar environment, and you wnat to reconstruct that environment freshly for each one. */ /* First you declare a type to hold the environment info, and functions to set it up and tear it down. */ struct data_buffer { /* We're just going to have couple of character buffer. Using setup/teardown functions is probably overkill for this case. You could also do file descriptors, complicated handles, temporary files, etc. */ char buffer1[512]; char buffer2[512]; }; /* The setup function needs to take a const struct testcase_t and return void* */ void * setup_data_buffer(const struct testcase_t *testcase) { struct data_buffer *db = malloc(sizeof(struct data_buffer)); /* If you had a complicated set of setup rules, you might behave differently here depending on testcase->flags or testcase->setup_data or even or testcase->name. */ /* Returning a NULL here would mean that we couldn't set up for this test, so we don't need to test db for null. */ return db; } /* The clean function deallocates storage carefully and returns true on success. */ int clean_data_buffer(const struct testcase_t *testcase, void *ptr) { struct data_buffer *db = ptr; if (db) { free(db); return 1; } return 0; } /* Finally, declare a testcase_setup_t with these functions. */ struct testcase_setup_t data_buffer_setup = { setup_data_buffer, clean_data_buffer }; /* Now let's write our test. */ void test_memcpy(void *ptr) { /* This time, we use the argument. */ struct data_buffer *db = ptr; /* We'll also introduce a local variable that might need cleaning up. */ char *mem = NULL; /* Let's make sure that memcpy does what we'd like. */ strcpy(db->buffer1, "String 0"); memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1)); tt_str_op(db->buffer1, ==, db->buffer2); /* Now we've allocated memory that's referenced by a local variable. The end block of the function will clean it up. */ mem = strdup("Hello world."); tt_assert(mem); /* Another rather trivial test. */ tt_str_op(db->buffer1, !=, mem); end: /* This time our end block has something to do. */ if (mem) free(mem); } /* ============================================================ */ /* Now we need to make sure that our tests get invoked. First, you take a bunch of related tests and put them into an array of struct testcase_t. */ struct testcase_t demo_tests[] = { /* Here's a really simple test: it has a name you can refer to it with, and a function to invoke it. */ { "strcmp", test_strcmp, }, /* The second test has a flag, "TT_FORK", to make it run in a subprocess, and a pointer to the testcase_setup_t that configures its environment. */ { "memcpy", test_memcpy, TT_FORK, &data_buffer_setup }, /* The array has to end with END_OF_TESTCASES. */ END_OF_TESTCASES }; /* Next, we make an array of testgroups. This is mandatory. Unlike more heavy-duty testing frameworks, groups can't next. */ struct testgroup_t groups[] = { /* Every group has a 'prefix', and an array of tests. That's it. */ { "demo/", demo_tests }, END_OF_GROUPS }; int main(int c, const char **v) { /* Finally, just call tinytest_main(). It lets you specify verbose or quiet output with --verbose and --quiet. You can list specific tests: tinytest-demo demo/memcpy or use a ..-wildcard to select multiple tests with a common prefix: tinytest-demo demo/.. If you list no tests, you get them all by default, so that "tinytest-demo" and "tinytest-demo .." mean the same thing. */ return tinytest_main(c, v, groups); } pgqd/lib/test/test_json.c0000664000401600040160000003611213175113172013760 0ustar cbecbe #include #include #include "test_common.h" static const char *simple_value(const char *json) { struct JsonContext *ctx; struct JsonValue *obj; static char buf[128]; const char *res; ctx = json_new_context(NULL, 128); obj = json_parse(ctx, json, strlen(json)); if (!obj) { snprintf(buf, sizeof(buf), "EPARSE: %s", json_strerror(ctx)); json_free_context(ctx); return buf; } else if (json_value_is_null(obj)) { res = "NULL"; } else if (json_value_is_bool(obj)) { bool val; if (!json_value_as_bool(obj, &val)) { res = "EBOOL"; } else { res = val ? "TRUE" : "FALSE"; } } else if (json_value_is_int(obj)) { int64_t val; if (!json_value_as_int(obj, &val)) { res = "ELONG"; } else { snprintf(buf, sizeof(buf), "INT:%lld", (long long)val); res = buf; } } else if (json_value_is_float(obj)) { double val; int i, j; if (!json_value_as_float(obj, &val)) { res = "EDBL"; } else { snprintf(buf, sizeof(buf), "FLOAT:%.17g", val); for (i = 0; buf[i]; i++) { if (!buf[i]) break; if (buf[i] >= '0' && buf[i] <= '9') continue; if (buf[i] == '+' || buf[i] == '-') continue; if (buf[i] == 'e' || buf[i] == 'E') continue; if (buf[i] == '.') continue; if (buf[i] == ',') buf[i] = '.'; else if (buf[i] & 0x80) { j = i; while (buf[j] & 0x80) j++; buf[i++] = '.'; memmove(buf + i, buf + j, strlen(buf + j) + 1); } } res = buf; } } else if (json_value_is_string(obj)) { const char *val; if (!json_value_as_string(obj, &val, NULL)) { res = "ESTR"; } else { snprintf(buf, sizeof(buf), "STR:%s", val); res = buf; } } else { res = "ENOSIMPLE"; } json_free_context(ctx); return res; } static const char *rerender_opts(const char *json, int opts) { struct JsonContext *ctx; struct JsonValue *obj; static char buf[1024]; struct MBuf dst; memset(buf, 0, sizeof buf); mbuf_init_fixed_writer(&dst, buf, sizeof(buf)); ctx = json_new_context(NULL, 128); json_set_options(ctx, opts); obj = json_parse(ctx, json, strlen(json)); if (!obj) { snprintf(buf, sizeof(buf), "EPARSE: %s", json_strerror(ctx)); json_free_context(ctx); return buf; } if (!json_render(&dst, obj)) return "ERENDER"; if (!mbuf_write_byte(&dst, 0)) return "ENUL"; json_free_context(ctx); return buf; } static const char *xrerender_opts(const char *xjson, int opts) { static char buf[1024]; char *s; const char *res; memset(buf, 0, sizeof buf); strlcpy(buf, xjson, sizeof(buf)); for (s = buf; *s; s++) { if (*s == '|') *s = '\\'; else if (*s == '\'') *s = '"'; } res = rerender_opts(buf, opts); strlcpy(buf, res, sizeof(buf)); for (s = buf; *s; s++) { if (*s == '\\') *s = '|'; else if (*s == '"') *s = '\''; } return buf; } static const char *rerender(const char *json) { return rerender_opts(json, 0); } static const char *xrerender(const char *xjson) { return xrerender_opts(xjson, 0); } static void test_json_basic(void *p) { str_check(simple_value("1"), "INT:1"); str_check(simple_value(" 10000 "), "INT:10000"); str_check(simple_value("true"), "TRUE"); str_check(simple_value(" true "), "TRUE"); str_check(simple_value("false"), "FALSE"); str_check(simple_value(" false "), "FALSE"); str_check(simple_value("null"), "NULL"); str_check(simple_value(" null "), "NULL"); str_check(simple_value("1.5"), "FLOAT:1.5"); str_check(simple_value(" 1.5 "), "FLOAT:1.5"); str_check(simple_value("\"abc\""), "STR:abc"); str_check(simple_value("\"\""), "STR:"); str_check(simple_value(" \"qq\\\"zz\\\\qq\" "), "STR:qq\"zz\\qq"); str_check(rerender("1"), "1"); str_check(rerender("[]"), "[]"); str_check(rerender("[1]"), "[1]"); str_check(rerender("[null, true, false]"), "[null,true,false]"); str_check(rerender("[1,2,[3,[],[4]]]"), "[1,2,[3,[],[4]]]"); str_check(rerender("[\"\", \"a\", \"\\\"\", \"a\\\"b\"]"), "[\"\",\"a\",\"\\\"\",\"a\\\"b\"]"); str_check(rerender("{}"), "{}"); str_check(rerender("{\"key\": \"val\"}"), "{\"key\":\"val\"}"); str_check(xrerender("{'key': 'val|'qqq'}"), "{'key':'val|'qqq'}"); str_check(xrerender("{'k': [1,2,-3], 'k2': {}}"), "{'k':[1,2,-3],'k2':{}}"); str_check(xrerender("'|b|f|n|r|t|/'"), "'|b|f|n|r|t/'"); str_check(xrerender("'|a'"), "EPARSE: Line #1: Invalid escape code"); str_check(xrerender("'𝄞'"), "'𝄞'"); str_check(xrerender("'|u0034'"), "'4'"); str_check(xrerender("'|u003'"), "EPARSE: Line #1: Invalid hex escape"); str_check(xrerender("'|u'"), "EPARSE: Line #1: Invalid hex escape"); str_check(xrerender("'|uD834|uDD1E'"), "'𝄞'"); str_check(xrerender("'|uD834 |uDD1E'"), "EPARSE: Line #1: Invalid UTF16 escape"); str_check(xrerender("'|uD834|uD834'"), "EPARSE: Line #1: Invalid UTF16 escape"); str_check(xrerender("'|uD834|u0100'"), "EPARSE: Line #1: Invalid UTF16 escape"); str_check(xrerender("'|uD834|uF000'"), "EPARSE: Line #1: Invalid UTF16 escape"); str_check(xrerender("'|uDD34|uDD00'"), "EPARSE: Line #1: Invalid UTF16 escape"); str_check(xrerender("'|uD834'"), "EPARSE: Line #1: Invalid UTF16 escape"); // check for \u2028 \u2029 special handling { const char utfesc[] = {'\'', 0xe2, 0x80, 0xa7, '|', 'u', '2', '0', '2', '8', '|', 'u', '2', '0', '2', '9', 0xe2, 0x80, 0xaa, '\'', 0}; str_check(xrerender("'|u2027|u2028|u2029|u202a'"), utfesc); } str_check(xrerender("["), "EPARSE: Line #1: Container still open"); str_check(xrerender("[{"), "EPARSE: Line #1: Container still open"); str_check(xrerender("\""), "EPARSE: Line #1: Unexpected end of string"); str_check(xrerender("[\""), "EPARSE: Line #1: Unexpected end of string"); str_check(xrerender("[,"), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender("[]]"), "EPARSE: Line #1: Unexpected symbol: ']'"); str_check(xrerender("tr"), "EPARSE: Line #1: Unexpected end of token"); str_check(xrerender("treu"), "EPARSE: Line #1: Invalid token"); str_check(xrerender("999999999999999999999999"), "EPARSE: Line #1: Number parse failed"); str_check(xrerender("99999999999999999999999999999999999999999999999999999999999999999999"), "EPARSE: Line #1: Number parse failed"); str_check(xrerender("99.999.999"), "EPARSE: Line #1: Number parse failed"); str_check(xrerender("[\n\"ln\n\","), "EPARSE: Line #3: Container still open"); str_check(xrerender("!"), "EPARSE: Line #1: Invalid symbol: '!'"); { const char badenc1[] = {'"', 0xc1, 0xbf, '"', 0}; const char badenc2[] = {'"', 0xe0, 0x9f, 0xbf, '"', 0}; const char badenc3[] = {'"', 0xf0, 0x8f, 0xbf, 0xbf, '"'}; const char badenc4[] = {'"', 0xf4, 0x90, 0x80, 0x80, '"', 0}; str_check(xrerender(badenc1), "EPARSE: Line #1: Invalid UTF8 sequence"); str_check(xrerender(badenc2), "EPARSE: Line #1: Invalid UTF8 sequence"); str_check(xrerender(badenc3), "EPARSE: Line #1: Invalid UTF8 sequence"); str_check(xrerender(badenc4), "EPARSE: Line #1: Invalid UTF8 sequence"); } end:; } static const char *render(struct JsonValue *obj) { static char buf[1024]; struct MBuf dst; mbuf_init_fixed_writer(&dst, buf, sizeof(buf)); if (!json_render(&dst, obj)) return "ERENDER"; if (!mbuf_write_byte(&dst, 0)) return "EMEM"; return buf; } static void test_json_render(void *p) { struct JsonContext *ctx; struct JsonValue *list, *dict; ctx = json_new_context(NULL, 128); tt_assert(ctx); list = json_new_list(ctx); tt_assert(json_list_append(list, json_new_null(ctx))); tt_assert(json_list_append(list, json_new_bool(ctx, 1))); tt_assert(json_list_append(list, json_new_bool(ctx, 0))); tt_assert(json_list_append(list, json_new_int(ctx, 600))); tt_assert(json_list_append(list, json_new_float(ctx, -0.5))); tt_assert(json_list_append(list, json_new_string(ctx, "q\\we"))); str_check(render(list), "[null,true,false,600,-0.5,\"q\\\\we\"]"); dict = json_new_dict(ctx); tt_assert(dict); str_check(render(dict), "{}"); tt_assert(json_dict_put(dict, "k", json_new_list(ctx))); str_check(render(dict), "{\"k\":[]}"); tt_assert(json_dict_put_null(dict, "n")); tt_assert(json_dict_put_bool(dict, "b", 1)); tt_assert(json_dict_put_int(dict, "i", 22)); tt_assert(json_dict_put_float(dict, "f", 1)); tt_assert(json_dict_put_string(dict, "s", "qwe")); str_check(render(dict), "{\"b\":true,\"f\":1.0,\"i\":22,\"k\":[],\"n\":null,\"s\":\"qwe\"}"); str_check(render(json_new_string(ctx, "\"\\ low:\a\b\f\n\r\t")), "\"\\\"\\\\ low:\\u0007\\b\\f\\n\\r\\t\""); list = json_new_list(ctx); tt_assert(json_list_append_null(list)); tt_assert(json_list_append_bool(list, false)); tt_assert(json_list_append_int(list, -1)); tt_assert(json_list_append_float(list, -2)); tt_assert(json_list_append_string(list, "qz\0foo")); str_check(render(list), "[null,false,-1,-2.0,\"qz\"]"); json_free_context(ctx); end:; } static void test_json_fetch(void *p) { struct JsonContext *ctx; struct JsonValue *list, *dict, *dict2, *obj; const char *json = "{\"intk\": 16, \"fk\": 1.1, \"sk\": \"qwe\", \"tk\": true, \"nk\": null, \"lst\":[], \"obj\": {}}"; bool bval; const char *sval; size_t slen; int64_t ival; double fval; ctx = json_new_context(NULL, 128); tt_assert(ctx); dict = json_parse(ctx, json, strlen(json)); tt_assert(dict); bval = false; tt_assert(json_dict_get_bool(dict, "tk", &bval)); tt_assert(bval == true); tt_assert(!json_dict_get_bool(dict, "nk", &bval)); tt_assert(bval == true); tt_assert(!json_dict_get_bool(dict, "missing", &bval)); tt_assert(bval == true); tt_assert(json_dict_get_opt_bool(dict, "nk", &bval)); tt_assert(bval == true); tt_assert(json_dict_get_opt_bool(dict, "missing", &bval)); tt_assert(bval == true); tt_assert(!json_dict_get_opt_bool(dict, "sk", &bval)); tt_assert(bval == true); ival = 8; tt_assert(json_dict_get_int(dict, "intk", &ival)); tt_assert(ival == 16); tt_assert(!json_dict_get_int(dict, "nk", &ival)); tt_assert(ival == 16); tt_assert(!json_dict_get_int(dict, "missing", &ival)); tt_assert(ival == 16); tt_assert(json_dict_get_opt_int(dict, "nk", &ival)); tt_assert(ival == 16); tt_assert(json_dict_get_opt_int(dict, "missing", &ival)); tt_assert(ival == 16); tt_assert(!json_dict_get_opt_int(dict, "sk", &ival)); tt_assert(ival == 16); fval = -9; tt_assert(json_dict_get_float(dict, "fk", &fval)); tt_assert(fval == 1.1); tt_assert(!json_dict_get_float(dict, "nk", &fval)); tt_assert(fval == 1.1); tt_assert(!json_dict_get_float(dict, "missing", &fval)); tt_assert(fval == 1.1); fval = -7; tt_assert(json_dict_get_opt_float(dict, "fk", &fval)); tt_assert(fval == 1.1); tt_assert(json_dict_get_opt_float(dict, "missing", &fval)); tt_assert(fval == 1.1); tt_assert(!json_dict_get_opt_float(dict, "obj", &fval)); tt_assert(fval == 1.1); sval = "x"; slen = 1; tt_assert(json_dict_get_string(dict, "sk", &sval, NULL)); str_check(sval, "qwe"); tt_assert(json_dict_get_string(dict, "sk", &sval, &slen)); tt_assert(slen == 3); tt_assert(!json_dict_get_string(dict, "nk", &sval, &slen)); str_check(sval, "qwe"); tt_assert(!json_dict_get_string(dict, "missing", &sval, NULL)); str_check(sval, "qwe"); sval = "z"; slen = 1; tt_assert(json_dict_get_opt_string(dict, "sk", &sval, NULL)); str_check(sval, "qwe"); tt_assert(json_dict_get_opt_string(dict, "sk", &sval, &slen)); tt_assert(slen == 3); tt_assert(json_dict_get_opt_string(dict, "missing", &sval, NULL)); str_check(sval, "qwe"); tt_assert(!json_dict_get_opt_string(dict, "fk", &sval, NULL)); str_check(sval, "qwe"); list = NULL; tt_assert(!json_dict_get_list(dict, "sk", &list)); tt_assert(list == NULL); tt_assert(json_dict_get_list(dict, "lst", &list)); tt_assert(list); tt_assert(json_value_type(list) == JSON_LIST); list = NULL; tt_assert(!json_dict_get_opt_list(dict, "fk", &list)); tt_assert(!list); tt_assert(json_dict_get_opt_list(dict, "lst", &list)); tt_assert(list); tt_assert(json_value_type(list) == JSON_LIST); tt_assert(json_dict_get_opt_list(dict, "nk", &list)); tt_assert(list); tt_assert(json_value_type(list) == JSON_LIST); dict2 = NULL; tt_assert(!json_dict_get_dict(dict, "sk", &dict2)); tt_assert(dict2 == NULL); tt_assert(json_dict_get_dict(dict, "obj", &dict2)); tt_assert(dict2); tt_assert(json_value_type(dict2) == JSON_DICT); dict2 = NULL; tt_assert(!json_dict_get_opt_dict(dict, "fk", &dict2)); tt_assert(!dict2); tt_assert(json_dict_get_opt_dict(dict, "obj", &dict2)); tt_assert(dict2); tt_assert(json_value_type(dict2) == JSON_DICT); tt_assert(json_dict_get_opt_dict(dict, "nk", &dict2)); tt_assert(dict2); tt_assert(json_value_type(dict2) == JSON_DICT); obj = NULL; tt_assert(!json_dict_get_value(dict, "missing", &obj)); tt_assert(obj == NULL); tt_assert(json_dict_get_value(dict, "nk", &obj)); tt_assert(obj); tt_assert(json_value_type(obj) == JSON_NULL); tt_assert(json_dict_get_value(dict, "obj", &obj)); tt_assert(obj); tt_assert(json_value_type(obj) == JSON_DICT); end: json_free_context(ctx); } static bool dict_walker(void *arg, struct JsonValue *key, struct JsonValue *val) { const char *k; int64_t v; int *counter = arg; if (!json_value_as_string(key, &k, NULL)) return false; if (!json_value_as_int(val, &v)) return false; if (atol(k) != v) return false; if (v != (*counter)++) return false; return true; } static bool list_walker(void *arg, struct JsonValue *elem) { int64_t v; int *counter = arg; if (!json_value_as_int(elem, &v)) return false; if (v != (*counter)++) return false; return true; } static void test_json_iter(void *p) { struct JsonContext *ctx; struct JsonValue *list, *dict; const char *json = "{\"3\": 3, \"1\": 1, \"2\": 2}"; const char *json2 = "[1,2,3]"; int counter; ctx = json_new_context(NULL, 128); tt_assert(ctx); dict = json_parse(ctx, json, strlen(json)); tt_assert(dict); int_check(json_value_size(dict), 3); counter = 1; tt_assert(json_dict_iter(dict, dict_walker, &counter)); list = json_parse(ctx, json2, strlen(json2)); tt_assert(list); int_check(json_value_size(list), 3); counter = 1; tt_assert(json_list_iter(list, list_walker, &counter)); end: json_free_context(ctx); } static void test_json_relax(void *p) { int rlx = JSON_PARSE_RELAXED; /* comments */ str_check(xrerender_opts("/* */ { 'a': 3 } // ", rlx), "{'a':3}"); str_check(xrerender_opts("// \n { //\n'a': 3//zzz\n} //", rlx), "{'a':3}"); str_check(xrerender_opts("/*\n/**/ { /**/ 'a': 3/*\n*/} /**/", rlx), "{'a':3}"); str_check(xrerender_opts("/* */ { 'a': [,], 'b':{,},'c':[1,],} // ", rlx), "{'a':[],'b':{},'c':[1]}"); /* extra comma */ str_check(xrerender_opts("[,,]", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("[1,,]", 0), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("[1,,]", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("[1,,1]", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("[],", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("{,,}", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("{},", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("{'a':1,,},", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); str_check(xrerender_opts("{'a':1,,'b':2},", rlx), "EPARSE: Line #1: Unexpected symbol: ','"); end:; } struct testcase_t json_tests[] = { { "basic", test_json_basic }, { "render", test_json_render }, { "fetch", test_json_fetch }, { "iter", test_json_iter }, { "relax", test_json_relax }, END_OF_TESTCASES }; pgqd/lib/test/tinytest.h0000664000401600040160000000751413175113172013644 0ustar cbecbe/* tinytest.h -- Copyright 2009 Nick Mathewson * * 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. */ #ifndef _TINYTEST_H #define _TINYTEST_H /** Flag for a test that needs to run in a subprocess. */ #define TT_FORK (1<<0) /** Runtime flag for a test we've decided to skip. */ #define TT_SKIP (1<<1) /** Internal runtime flag for a test we've decided to run. */ #define _TT_ENABLED (1<<2) /** If you add your own flags, make them start at this point. */ #define TT_FIRST_USER_FLAG (1<<3) typedef void (*testcase_fn)(void *); struct testcase_t; /** Functions to initialize/teardown a structure for a testcase. */ struct testcase_setup_t { /** Return a new structure for use by a given testcase. */ void *(*setup_fn)(const struct testcase_t *); /** Clean/free a structure from setup_fn. Return 1 if ok, 0 on err. */ int (*cleanup_fn)(const struct testcase_t *, void *); }; /** A single test-case that you can run. */ struct testcase_t { const char *name; /**< An identifier for this case. */ testcase_fn fn; /**< The function to run to implement this case. */ unsigned long flags; /**< Bitfield of TT_* flags. */ const struct testcase_setup_t *setup; /**< Optional setup/cleanup fns*/ void *setup_data; /**< Extra data usable by setup function */ }; #define END_OF_TESTCASES { NULL, NULL, 0, NULL, NULL } /** A group of tests that are selectable together. */ struct testgroup_t { const char *prefix; /**< Prefix to prepend to testnames. */ struct testcase_t *cases; /** Array, ending with END_OF_TESTCASES */ }; #define END_OF_GROUPS { NULL, NULL} /** Implementation: called from a test to indicate failure, before logging. */ void _tinytest_set_test_failed(void); /** Implementation: called from a test to indicate that we're skipping. */ void _tinytest_set_test_skipped(void); /** Implementation: return 0 for quiet, 1 for normal, 2 for loud. */ int _tinytest_get_verbosity(void); /** Implementation: Set a flag on tests matching a name; returns number * of tests that matched. */ int _tinytest_set_flag(struct testgroup_t *, const char *, unsigned long); /** Set all tests in 'groups' matching the name 'named' to be skipped. */ #define tinytest_skip(groups, named) \ _tinytest_set_flag(groups, named, TT_SKIP) /** Run a single testcase in a single group. */ int testcase_run_one(const struct testgroup_t *,const struct testcase_t *); /** Run a set of testcases from an END_OF_GROUPS-terminated array of groups, as selected from the command line. */ int tinytest_main(int argc, const char **argv, struct testgroup_t *groups); #endif pgqd/lib/test/test_socket.c0000664000401600040160000000313213175113172014273 0ustar cbecbe#include #include #include "test_common.h" static const char *ntop(int af, const void *src) { static char buf[128]; const char *res; res = inet_ntop(af, src, buf, sizeof(buf)); return res ? res : "NULL"; } static void test_ntop(void *z) { static const uint8_t data[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; str_check(ntop(AF_INET, data), "1.2.3.4"); str_check(ntop(AF_INET6, data), "102:304:506:708:90a:b0c:d0e:f10"); end:; } static const char *pton(int af, const char *s) { static char str[128]; unsigned char buf[128]; int res; int len = (af == AF_INET) ? 4 : 16; memset(buf, 0xCC, sizeof(buf)); res = inet_pton(af, s, buf); if (res < 0) return "EAFBAD"; if (res == 0) return "FAIL"; if (buf[len] != 0xCC || buf[len + 1] != 0xCC) return "EOVER"; if (buf[len - 1] == 0xCC || buf[0] == 0xCC) return "EUNDER"; s = inet_ntop(af, buf, str, sizeof(str)); return s ? s : "NULL"; } static void test_pton(void *z) { str_check(pton(AF_INET, "127.0.0.255"), "127.0.0.255"); str_check(pton(AF_INET, "127.0.0"), "FAIL"); str_check(pton(AF_INET, "127.1.1.a"), "FAIL"); str_check(pton(AF_INET, "127.1.1.300"), "FAIL"); str_check(pton(AF_INET6, "0001:0002:ffff:4444:5555:6666:7777:8888"), "1:2:ffff:4444:5555:6666:7777:8888"); str_check(pton(AF_INET6, "::"), "::"); str_check(pton(AF_INET6, "F00F::5060"), "f00f::5060"); str_check(pton(AF_INET6, "F00F::127.0.0.1"), "f00f::7f00:1"); str_check(pton(AF_INET6, "::1:2:3:4:5:6:7:8"), "FAIL"); end:; } struct testcase_t socket_tests[] = { { "inet_ntop", test_ntop }, { "inet_pton", test_pton }, END_OF_TESTCASES }; pgqd/lib/test/test_cbtree.c0000664000401600040160000000624113175113172014253 0ustar cbecbe #include #include #include #include "test_common.h" static char *OK = "OK"; struct MyNode { char str[64]; int len; }; static size_t my_getkey(void *ctx, void *obj, const void **dst_p) { struct MyNode *node = obj; *dst_p = node->str; return node->len; } static struct MyNode *make_node(int value) { struct MyNode *node = malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); snprintf(node->str, sizeof(node->str), "%d", value); node->len = strlen(node->str); return node; } static bool my_node_free(void *ctx, void *obj) { free(obj); return true; } /* * Test tree sanity */ /* * checking operations */ static const char *my_search(struct CBTree *tree, int value) { struct AANode *res; char buf[64]; snprintf(buf, sizeof(buf), "%d", value); res = cbtree_lookup(tree, buf, strlen(buf)); return res ? OK : "not found"; } static const char *my_insert(struct CBTree *tree, int value) { struct MyNode *my = make_node(value); if (!cbtree_insert(tree, my)) return "insert failed"; return my_search(tree, value); } static const char *my_remove(struct CBTree *tree, int value) { struct MyNode *my; char buf[64]; snprintf(buf, sizeof(buf), "%d", value); my = cbtree_lookup(tree, buf, strlen(buf)); if (!my) return "nonexsist element"; cbtree_delete(tree, buf, strlen(buf)); if (cbtree_lookup(tree, buf, strlen(buf)) != NULL) return "still found"; return OK; } /* * Simple opeartions. */ static void test_cbtree_basic(void *p) { struct CBTree *tree; int i; tree = cbtree_create(my_getkey, my_node_free, NULL, NULL); str_check(my_search(tree, 1), "not found"); for (i = 0; i < 15; i++) { str_check(my_insert(tree, i), "OK"); } for (i = -1; i > -15; i--) { str_check(my_insert(tree, i), "OK"); } for (i = 30; i < 45; i++) { str_check(my_insert(tree, i), "OK"); } for (i = 15; i < 30; i++) { str_check(my_insert(tree, i), "OK"); } for (i = -14; i < 45; i++) { str_check(my_remove(tree, i), "OK"); } end: cbtree_destroy(tree); } /* * randomized test */ #define RSIZE 3000 static int get_next(bool with_stat, bool added[]) { int r = pseudo_random_range(RSIZE); int i = r; while (1) { if (added[i] == with_stat) return i; if (++i >= RSIZE) i = 0; if (i == r) return -1; } } static void test_cbtree_random(void *p) { bool is_added[RSIZE]; int prefer_remove = 0; /* 0 - insert, 1 - delete */ int n; int op; /* 0 - insert, 1 - delete */ struct CBTree *tree; unsigned long long total = 0; memset(is_added, 0, sizeof(is_added)); tree = cbtree_create(my_getkey, my_node_free, NULL, NULL); while (total < 20000) { int r = pseudo_random_range(16); if (prefer_remove) op = r > 5; else op = r > 10; /* op = 0; */ n = get_next(op, is_added); if (n < 0) { if (prefer_remove == op) { prefer_remove = !prefer_remove; } continue; } if (op == 0) { str_check(my_insert(tree, n), "OK"); is_added[n] = 1; } else { str_check(my_remove(tree, n), "OK"); is_added[n] = 0; } total++; } end: cbtree_destroy(tree); } struct testcase_t cbtree_tests[] = { { "basic", test_cbtree_basic }, { "random", test_cbtree_random }, END_OF_TESTCASES }; pgqd/lib/test/test_crypto.c0000664000401600040160000013374213175113172014336 0ustar cbecbe #include #include "test_common.h" #define str_check(a, b) tt_str_op(a, ==, b) #define tt_stri_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \ (strcasecmp(_val1,_val2) op 0),"<%s>") #define stri_check(a, b) tt_stri_op(a, ==, b) #include #include #include #include #include #include #include #include #include #include #include static const char *mkhex(const uint8_t *src, int len) { static char buf[1024 + 1]; static const char hextbl[] = "0123456789abcdef"; int i; for (i = 0; i < len; i++) { buf[i*2] = hextbl[src[i] >> 4]; buf[i*2+1] = hextbl[src[i] & 15]; } buf[i*2] = 0; return buf; } static int hexval(char v) { if (v >= '0' && v <= '9') return v - '0'; if (v >= 'a' && v <= 'f') return v - 'a' + 10; if (v >= 'A' && v <= 'F') return v - 'A' + 10; return -1; } static uint8_t *fromhex(const char *input, int len) { uint8_t *res; const char *s = input; int i, b, b1, b2; res = malloc(len+1); if (!res) return NULL; for (i = 0; i < len; i++) { if (*s == '\0') s = input; b1 = hexval(*s++); b2 = hexval(*s++); b = (b1 << 4) | b2; if (b < 0) { free(res); return NULL; } res[i] = b; } return res; } static const char *run_hash(const char *str, const char *hexstr, const struct DigestInfo *impl) { struct DigestContext *ctx; uint8_t res[512]; uint8_t res2[512]; int i, len, step; int reslen; uint8_t *buf = NULL; if (hexstr) { len = strlen(hexstr) / 2; buf = fromhex(hexstr, len); if (!buf) return "NOMEM"; str = (char *)buf; } else { len = strlen(str); } ctx = digest_new(impl, NULL); if (!ctx) return "NOMEM"; reslen = digest_result_len(ctx); digest_update(ctx, str, len); digest_final(ctx, res); digest_reset(ctx); step = 3; for (i = 0; i < len; i += step) digest_update(ctx, str+i, (i + step <= len) ? (step) : (len - i)); digest_final(ctx, res2); digest_free(ctx); if (buf) free(buf); if (memcmp(res, res2, reslen) != 0) return "FAIL"; return mkhex(res, reslen); } /* * MD5 */ static const char *run_md5(const char *str) { return run_hash(str, NULL, digest_MD5()); } static void test_md5(void *ptr) { str_check(run_md5(""), "d41d8cd98f00b204e9800998ecf8427e"); str_check(run_md5("a"), "0cc175b9c0f1b6a831c399e269772661"); str_check(run_md5("abc"), "900150983cd24fb0d6963f7d28e17f72"); str_check(run_md5("message digest"), "f96b697d7cb7938d525a2f31aaf161d0"); str_check(run_md5("abcdefghijklmnopqrstuvwxyz"), "c3fcd3d76192e4007dfb496cca67e13b"); str_check(run_md5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), "d174ab98d277d9f5a5611c2c9f419d9f"); str_check(run_md5("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "57edf4a22be3c955ac49da2e2107b67a"); end:; } /* * SHA1 */ static const char *run_sha1(const char *str) { return run_hash(str, NULL, digest_SHA1()); } static void test_sha1(void *ptr) { str_check(run_sha1(""), "da39a3ee5e6b4b0d3255bfef95601890afd80709"); str_check(run_sha1("a"), "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); str_check(run_sha1("abc"), "a9993e364706816aba3e25717850c26c9cd0d89d"); str_check(run_sha1("message digest"), "c12252ceda8be8994d5fa0290a47231c1d16aae3"); str_check(run_sha1("abcdefghijklmnopqrstuvwxyz"), "32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); str_check(run_sha1("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), "761c457bf73b14d27e9e9265c46f4b4dda11f940"); str_check(run_sha1("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "50abf5706a150990a08b2c5ea40fa0e585554732"); end:; } /* * SHA224 */ static const char *run_sha224(const char *str) { return run_hash(str, NULL, digest_SHA224()); } static void test_sha224(void *ptr) { str_check(run_sha224(""), "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); str_check(run_sha224("a"), "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"); str_check(run_sha224("abc"), "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"); str_check(run_sha224("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525"); str_check(run_sha224("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e"); end:; } /* * SHA256 */ static const char *run_sha256(const char *str) { return run_hash(str, NULL, digest_SHA256()); } static void test_sha256(void *ptr) { str_check(run_sha256(""), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); str_check(run_sha256("a"), "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"); str_check(run_sha256("abc"), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); str_check(run_sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); str_check(run_sha256("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e"); end:; } /* * SHA384 */ static const char *run_sha384(const char *str) { return run_hash(str, NULL, digest_SHA384()); } static void test_sha384(void *ptr) { str_check(run_sha384(""), "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"); str_check(run_sha384("a"), "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"); str_check(run_sha384("abc"), "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"); str_check(run_sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b"); str_check(run_sha384("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039"); str_check(run_sha384("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"), "3d208973ab3508dbbd7e2c2862ba290ad3010e4978c198dc4d8fd014e582823a89e16f9b2a7bbc1ac938e2d199e8bea4"); end:; } /* * SHA512 */ static const char *run_sha512(const char *str) { return run_hash(str, NULL, digest_SHA512()); } static void test_sha512(void *ptr) { str_check(run_sha512(""), "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); str_check(run_sha512("a"), "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"); str_check(run_sha512("abc"), "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); str_check(run_sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"); str_check(run_sha512("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"), "930d0cefcb30ff1133b6898121f1cf3d27578afcafe8677c5257cf069911f75d8f5831b56ebfda67b278e66dff8b84fe2b2870f742a580d8edb41987232850c9"); str_check(run_sha512("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"); end:; } /* * SHA3-224 */ static const char *run_sha3_224(const char *hex) { return run_hash(NULL, hex, digest_SHA3_224()); } static void test_sha3_224(void *ptr) { /* KeccakCodePackage: ShortMsgKAT_SHA3-224.txt */ stri_check(run_sha3_224(""), "6B4E03423667DBB73B6E15454F0EB1ABD4597F9A1B078E3F5B5A6BC7"); stri_check(run_sha3_224("CC"), "DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"); stri_check(run_sha3_224("41FB"), "BFF295861DAEDF33E70519B1E2BCB4C2E9FE3364D789BC3B17301C15"); stri_check(run_sha3_224("1F877C"), "14889DF49C076A9AF2F4BCB16339BCC45A24EBF9CE4DCDCE7EC17217"); stri_check(run_sha3_224("C1ECFDFC"), "A33C58DF8A8026F0F9591966BD6D00EED3B1E829580AB9BE268CAF39"); stri_check(run_sha3_224("21F134AC57"), "10E580A32199596169331AD43CFCF10264F81565037040028A06B458"); stri_check(run_sha3_224("C6F50BB74E29"), "FE52C30C95C1E5193207E97D355FDE09453482708C0876AA961508F0"); stri_check(run_sha3_224("119713CC83EEEF"), "8B449849CB7C4776C593DE58FD5C2E322CB5316BE08A75057A01ED6A"); stri_check(run_sha3_224("4A4F202484512526"), "01386CDD70589B3B34941EFE16B85071E9BA948179922044F640868E"); /* 1088 */ stri_check(run_sha3_224("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"), "64D78817714FE05272D3805E6E19056B1649036CDCD5094FD1CC890A"); /* 1096 */ stri_check(run_sha3_224("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"), "2C4E7C537D0E2AF2261A669BC24BD0DF16D2C72A7F98D7A5EF6A8150"); /* 2040 */ stri_check(run_sha3_224("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"), "94689EA9F347DDA8DD798A858605868743C6BD03A6A65C6085D52BED"); end:; } /* * SHA3-256 */ static const char *run_sha3_256(const char *hex) { return run_hash(NULL, hex, digest_SHA3_256()); } static void test_sha3_256(void *ptr) { /* KeccakCodePackage: ShortMsgKAT_SHA3-256.txt */ stri_check(run_sha3_256(""), "A7FFC6F8BF1ED76651C14756A061D662F580FF4DE43B49FA82D80A4B80F8434A"); stri_check(run_sha3_256("CC"), "677035391CD3701293D385F037BA32796252BB7CE180B00B582DD9B20AAAD7F0"); stri_check(run_sha3_256("41FB"), "39F31B6E653DFCD9CAED2602FD87F61B6254F581312FB6EEEC4D7148FA2E72AA"); stri_check(run_sha3_256("1F877C"), "BC22345E4BD3F792A341CF18AC0789F1C9C966712A501B19D1B6632CCD408EC5"); stri_check(run_sha3_256("C1ECFDFC"), "C5859BE82560CC8789133F7C834A6EE628E351E504E601E8059A0667FF62C124"); stri_check(run_sha3_256("21F134AC57"), "55BD9224AF4EED0D121149E37FF4D7DD5BE24BD9FBE56E0171E87DB7A6F4E06D"); stri_check(run_sha3_256("C6F50BB74E29"), "AE0CBC757D4AB088E172ABFD8746289950F92D38A25295658DBF744B5635AF04"); stri_check(run_sha3_256("119713CC83EEEF"), "E340C9A44373EFCC212F3CB66A047AC34C87FF1C58C4A14B16A2BFC34698BB1D"); stri_check(run_sha3_256("4A4F202484512526"), "BA4FB009D57A5CEB85FC64D54E5C55A55854B41CC47AD15294BC41F32165DFBA"); /* 1088 */ stri_check(run_sha3_256("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"), "DF673F4105379FF6B755EEAB20CEB0DC77B5286364FE16C59CC8A907AFF07732"); /* 1096 */ stri_check(run_sha3_256("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"), "D52432CF3B6B4B949AA848E058DCD62D735E0177279222E7AC0AF8504762FAA0"); /* 2040 */ stri_check(run_sha3_256("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"), "C11F3522A8FB7B3532D80B6D40023A92B489ADDAD93BF5D64B23F35E9663521C"); end:; } /* * SHA3-384 */ static const char *run_sha3_384(const char *hex) { return run_hash(NULL, hex, digest_SHA3_384()); } static void test_sha3_384(void *ptr) { /* KeccakCodePackage: ShortMsgKAT_SHA3-384.txt */ stri_check(run_sha3_384(""), "0C63A75B845E4F7D01107D852E4C2485C51A50AAAA94FC61995E71BBEE983A2AC3713831264ADB47FB6BD1E058D5F004"); stri_check(run_sha3_384("CC"), "5EE7F374973CD4BB3DC41E3081346798497FF6E36CB9352281DFE07D07FC530CA9AD8EF7AAD56EF5D41BE83D5E543807"); stri_check(run_sha3_384("41FB"), "1DD81609DCC290EFFD7AC0A95D4A20821580E56BD50DBD843920650BE7A80A1719577DA337CFDF86E51C764CAA2E10BD"); stri_check(run_sha3_384("1F877C"), "14F6F486FB98ED46A4A198040DA8079E79E448DAACEBE905FB4CF0DF86EF2A7151F62FE095BF8516EB0677FE607734E2"); stri_check(run_sha3_384("C1ECFDFC"), "D92BBD604BDD24B9889508F8558B13E96595AC90BC8A441DAF9B51D6ABC14FFD0835FB9366E3912504264CE87E421CB8"); stri_check(run_sha3_384("21F134AC57"), "E248D6FF342D35A30EC230BA51CDB161025D6F1C251ACA6AE3531F0682C164A1FC0725B1BEFF808A200C131557A22809"); stri_check(run_sha3_384("C6F50BB74E29"), "D6DD2ED08C1F644857A15DAFAF80538BEE597278C9ABE047BFBABFB8B1FCB7543E80AE9F7143D00F4DAAF39B138AB3FF"); stri_check(run_sha3_384("119713CC83EEEF"), "49CA1EB8D71D1FDC7A72DAA320C8F9CA543671C2CB8FE9B2638A8416DF50A790A50D0BB6B88741D7816D6061F46AEA89"); stri_check(run_sha3_384("4A4F202484512526"), "89DBF4C39B8FB46FDF0A6926CEC0355A4BDBF9C6A446E140B7C8BD08FF6F489F205DAF8EFFE160F437F67491EF897C23"); /* 1088 */ stri_check(run_sha3_384("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"), "CAD2D28FBDCC3A5D71FB3ADCEEC52313AD41D4FF1F915CAA34EE127839DBF2E9A7B06E1C4ECD6255926C16C06E51EFD0"); /* 1096 */ stri_check(run_sha3_384("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"), "5B192EBAB47215A8E9FB8E4D561B220B1DC36707A3F085F7BB0175335C393251E3467F945570420C743365D0F09B9E09"); /* 2040 */ stri_check(run_sha3_384("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"), "128DC611762BE9B135B3739484CFAADCA7481D68514F3DFD6F5D78BB1863AE68130835CDC7061A7ED964B32F1DB75EE1"); end:; } /* * SHA3-512 */ static const char *run_sha3_512(const char *hex) { return run_hash(NULL, hex, digest_SHA3_512()); } static void test_sha3_512(void *ptr) { /* KeccakCodePackage: ShortMsgKAT_SHA3-512.txt */ stri_check(run_sha3_512(""), "A69F73CCA23A9AC5C8B567DC185A756E97C982164FE25859E0D1DCC1475C80A615B2123AF1F5F94C11E3E9402C3AC558F500199D95B6D3E301758586281DCD26"); stri_check(run_sha3_512("CC"), "3939FCC8B57B63612542DA31A834E5DCC36E2EE0F652AC72E02624FA2E5ADEECC7DD6BB3580224B4D6138706FC6E80597B528051230B00621CC2B22999EAA205"); stri_check(run_sha3_512("41FB"), "AA092865A40694D91754DBC767B5202C546E226877147A95CB8B4C8F8709FE8CD6905256B089DA37896EA5CA19D2CD9AB94C7192FC39F7CD4D598975A3013C69"); stri_check(run_sha3_512("1F877C"), "CB20DCF54955F8091111688BECCEF48C1A2F0D0608C3A575163751F002DB30F40F2F671834B22D208591CFAF1F5ECFE43C49863A53B3225BDFD7C6591BA7658B"); stri_check(run_sha3_512("C1ECFDFC"), "D4B4BDFEF56B821D36F4F70AB0D231B8D0C9134638FD54C46309D14FADA92A2840186EED5415AD7CF3969BDFBF2DAF8CCA76ABFE549BE6578C6F4143617A4F1A"); stri_check(run_sha3_512("21F134AC57"), "584219A84E8796076BF1178B14B9D1E2F96A4B4EF11F10CC516FBE1A29639D6BA74FB92815F9E3C5192ED4DCA20AEA5B109D52237C9956401FD44B221F82AB37"); stri_check(run_sha3_512("C6F50BB74E29"), "4345B92A2AB7EADB6A24EE1D175AC258CCF2F694AC09EC9D47399E4D96F61F30B322C5438C51BACD0D597D00471A41ED8E9C9F146BBC807E6BC385F850FBABFE"); stri_check(run_sha3_512("119713CC83EEEF"), "50081C93BF73ECC54A5FFE43FC14F8BAEEDBE7DA0302AC984C9E668389886BD064BAB26DDCB616EB4E0E726042B19F3FD50BDD0D2C5B34892E00E6F399DE254F"); stri_check(run_sha3_512("4A4F202484512526"), "150D787D6EB49670C2A4CCD17E6CCE7A04C1FE30FCE03D1EF2501752D92AE04CB345FD42E51038C83B2B4F8FD438D1B4B55CC588C6B913132F1A658FB122CB52"); /* 1088 */ stri_check(run_sha3_512("B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04"), "2E293765022D48996CE8EFF0BE54E87EFB94A14C72DE5ACD10D0EB5ECE029CADFA3BA17A40B2FFA2163991B17786E51CABA79E5E0FFD34CF085E2A098BE8BACB"); /* 1096 */ stri_check(run_sha3_512("04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D"), "BE8E14B6757FFE53C9B75F6DDE9A7B6C40474041DE83D4A60645A826D7AF1ABE1EEFCB7B74B62CA6A514E5F2697D585BFECECE12931BBE1D4ED7EBF7B0BE660E"); /* 2040 */ stri_check(run_sha3_512("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"), "6E8B8BD195BDD560689AF2348BDC74AB7CD05ED8B9A57711E9BE71E9726FDA4591FEE12205EDACAF82FFBBAF16DFF9E702A708862080166C2FF6BA379BC7FFC2"); end:; } /* * SHAKE128 */ static const char *run_variable(const char *hex, const struct DigestInfo *mdinfo) { struct DigestContext *ctx; uint8_t res[512]; int len; unsigned reslen; uint8_t *buf = NULL; len = strlen(hex) / 2; buf = fromhex(hex, len); ctx = digest_new(mdinfo, NULL); if (!ctx) return "NOMEM"; digest_update(ctx, buf, len); free(buf); reslen = 0; while (reslen < sizeof(res)) { digest_final(ctx, res + reslen); reslen += digest_result_len(ctx); } digest_free(ctx); return mkhex(res, reslen); } static const char *run_shake128(const char *hex) { return run_variable(hex, digest_SHAKE128()); } static void test_shake128(void *ptr) { /* KeccakCodePackage: ShortMsgKAT_SHAKE128.txt */ stri_check(run_shake128(""), "7F9C2BA4E88F827D616045507605853ED73B8093F6EFBC88EB1A6EACFA66EF263CB1EEA988004B93103CFB0AEEFD2A686E01FA4A58E8A3639CA8A1E3F9AE57E235B8CC873C23DC62B8D260169AFA2F75AB916A58D974918835D25E6A435085B2BADFD6DFAAC359A5EFBB7BCC4B59D538DF9A04302E10C8BC1CBF1A0B3A5120EA17CDA7CFAD765F5623474D368CCCA8AF0007CD9F5E4C849F167A580B14AABDEFAEE7EEF47CB0FCA9767BE1FDA69419DFB927E9DF07348B196691ABAEB580B32DEF58538B8D23F87732EA63B02B4FA0F4873360E2841928CD60DD4CEE8CC0D4C922A96188D032675C8AC850933C7AFF1533B94C834ADBB69C6115BAD4692D8619F90B0CDF8A7B9C264029AC185B70B83F2801F2F4B3F70C593EA3AEEB613A7F1B1DE33FD75081F592305F2E4526EDC09631B10958F464D889F31BA010250FDA7F1368EC2967FC84EF2AE9AFF268E0B1700AFFC6820B523A3D917135F2DFF2EE06BFE72B3124721D4A26C04E53A75E30E73A7A9C4A95D91C55D495E9F51DD0B5E9D83C6D5E8CE803AA62B8D654DB53D09B8DCFF273CDFEB573FAD8BCD45578BEC2E770D01EFDE86E721A3F7C6CCE275DABE6E2143F1AF18DA7EFDDC4C7B70B5E345DB93CC936BEA323491CCB38A388F546A9FF00DD4E1300B9B2153D2041D205B443E41B45A653F2A5C4492C1ADD544512DDA2529833462B71A41A45BE97290B6F"); stri_check(run_shake128("CC"), "4DD4B0004A7D9E613A0F488B4846F804015F0F8CCDBA5F7C16810BBC5A1C6FB254EFC81969C5EB49E682BABAE02238A31FD2708E418D7B754E21E4B75B65E7D39B5B42D739066E7C63595DAF26C3A6A2F7001EE636C7CB2A6C69B1EC7314A21FF24833EAB61258327517B684928C7444380A6EACD60A6E9400DA37A61050E4CD1FBDD05DDE0901EA2F3F67567F7C9BF7AA53590F29C94CB4226E77C68E1600E4765BEA40B3644B4D1E93EDA6FB0380377C12D5BB9DF4728099E88B55D820C7F827034D809E756831A334C078FC28ACB76B5ADB3BFF6DD659CAA49CC34F726880F293BD3FC132027AE7602242064EFEC4D9D656E069D4DFAE0B40C1D6B6CDB21D89D00E168B0B74D72EBB3B672B57AF3E99C85DA2F41CE70672CD0E0521678FC56EAB6314A0B3AF8B724376C01433D84943A73AF703D293634BC24322992756EE261FFF0D71BFFB8AEBF1026A6A345F2EAED505BC7E02498A3225FC91499DD5F5E30E386557C5FE0A88BC2337C80D7EA42B60622960230577CE800CB63594F619B7DE31E026429B7648C5835AFC00559FA4C7695D6DD9F7B2537A265E9AF7A2C986F8B60E7DC6EB3C4D805A6EEFB6FBB5BFDE21ED7E41CFDBEB02B0BAB76F9998BA1E52815A246B084EFAE7960AFFC2BA5C647E7CC05EF8120568432DFDE1D7246473304808985600A1AFC20B99185AF25E89DC2EC6F4880DC79BAD50DFFCC9EA"); stri_check(run_shake128("41FB"), "09C9652BB968996A35E4814E27587131F53FD01AB9FE83758ACEB8134FCECA24C84F592CEE43A4476E8853FCAB7DAFEF7B60ECFEBFD70DFCF587B3AF358A286FE3713BF4735A84975BB65E3586C81EA716BFB999626DC973A495A6E0024061387D628E9E59DFD2B39C68C8CEAD665AB43F6D2625A10630761DFB60276EA97B280442462246C6D74A1960A8419A76A37B68449A9E427D6A7EC1FBDF4760847AD6F6F5A08CEFB767CAEB6C2382F4F3D0E49DE4428CD4240635C9136911A82FF0B9C74569A1B7C8AF72AB1EA5F2F6F6A45E3BB08229ADDFA916B18A74F7939C5130152AC8343A10694154FDC6E1570EC7ECABBB01EDDC92EF0BB1B3DB914C74CCE399ACC9B766FD7494B2EF27AC57B80D52535942D55E2DBFAA61CDF3F48759AA612DED11421855AD15FFAB91462A56F873BBAF4FE88457A47B6C0594818D0A9189895239C1429ED8754EEE5498F4D0FB6C9D0DF0EB5316289E72C6AAEB8C61317B409156D4221CE6CFC7C5F39272D87C2D884F88F1B8B3C05CA9E235ED92C7DD7806CDADA7166CC1B9107DA5E6536D4FF111BF9199D6B72AC17D874323D68D76AEC4650F1A4B067C50215362201A7F71116BF6633AF08D712804B83F08A5DC7CCD4315963106D50453D44EFF59C9C652F4A924BE93C0B958EA286B0A4B597899A28C9BD5419C042668AA7B0CFCAC4CDF9260F2824ABF3EE79FEF53EBE3C36DF831"); stri_check(run_shake128("C1ECFDFC"), "B5EB987E7CBFC7C9D140AFD20B500E30F2F71188BCE885951F22FBC35DE40E7411311BC8479F895A069600483751295F5C3855474D65436B347608583125A6BD41CA30DC99CB342B72A96F8A2213E98A16BBB9E38A141B4FBA68C1A723D1D578A91B4A1BAFD03B25BD3CFB6D4E1E044637889C6D7AF0209DBB5E10837D5C5991D2766012E7E86A82838B455B96D882B7E25C9072A66DA22B0ACB992FD3CCF5FBC28625B896BDF8D4B7358901D12698FD823FE09AFB4F238631EE1277752F2900E38C7FC163381A01601DBDE8CD30A9A8EE8353DEF6C610B50962D0EC1F4F3EEC2AFD7FCD5296E299C23005960C1AABF3408EDE96DE18E10FDE99774B5BD33092639D8BE9D872130C96311EB6DCA82CC28A62C01E3F9C454251D87FA890284C06187F904CF23CCC62690628C590BC8B84EEE68371D3412DCB5648A634F2359951CD011028E61C28A33EC4E31550C1C0E4123D1091E22D56BD587E73B7DD43C1B431D9547398C5E7EB6C682EA2FD758C86C1222C0D6AB236B8106DAC19338E86CB8F6EC0FB607D70C938CAC172C80079B018BB62939546505B8B6E146A3AD7A35F20D7F9A353F1E6535C23AE93CE8F78C045402E707A664F5918E6836D343240E6112EFA29DFC4D18778C91E2B8530E4FF6E4947623D31DAFAC9B0D194E36C44E10B1A846935684F89ADB44782E8FFF2E3E62B65D18140F10D36CD817F835EB0C0"); /* 1024 */ stri_check(run_shake128("2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817"), "5E0693E64DBE678055A31D80DA85A81A1BA5EB1CB6394B7B1AA2ADC2B8C1FEE4F41DFF3623502FCC349D4B2BEBBC1311EF291EF47F490C6FC1323075B519C87DBD77CB94C213EB548278A6C11B0C321C941FBEB9742B690A9777875D8A1FD0627B5CE3679A6DBCC8B53D775E73AC9F951749FC353EEE7220CAB40F4F7C2EEA311CD0B50974AF188EA9742509C4121FC07A70FC28F0633B56BABB451554BDF86396594B57B1F88DFA07DD4F8C47AB5FFBAD03DA1DA642EF4A6E75A952E8ACB3989136B8A792E6F33B24DA2CC4E422192EEBAF121561BD33A6D79DF6517B3F001E674F39BB98D9AA0E15226389B17655E7B2796D75EDE0430A0E7D07039B8E35642570F6457F8344A59DBDD5128F74F9AB83CD152DB17987019AA0B27B6E7951CEF67B574D05535CEB5EB023A39B5731B6AFE6CBB8BFEE0F71819D791C288BFE9EBE6706778FE62A80AEF22CB9C0920938502F30A20FB4A4822D0C0D42D50D67FA6A76EF919C985C6306FEA73A3BF41006F0FE60C0D92B80106D8C715B97E35E39FA8ECA59220B86C2C46B9C61EEBCE07DA4B64CAD247A67112F6548AA693BFC4BFC9AE78144571CBE7B81B83C8832938A71FD3AC6B8E4AECF17FD2848FD9558DC5DDB261CCAAF083AFFA341EA295C7264F35734F5179038AB70C90E03BF53E3FF438A9C0655EC38F5B9DAB7A4C3BA852A2A0593C80999818B460015C79093B1D6"); /* 2040 */ stri_check(run_shake128("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"), "14236E75B9784DF4F57935F945356CBE383FE513ED30286F91060759BCB0EF4BAAC858ECAE7C6E7EDD498F01A082B63FA57D22540231E2E25C83EFB3B3F2953A5F674502AB635226446B84937643DCD5789EE73F1D734BC8FE5F7F0883AB10961B9A31FF60DEE16159BC6982EFB08545984BF71FED1C4CD81C0914B4C19FCFEEF54AF4BBE372F18CFCD3A18657F5B9450F99A78F0FA2C3CDCA7461C4ED7569536883B66CD87E9C200962902EAA16A54DB6A0A5CC26D889038C0760810B5BB4F33F1E5D639B6F9BC7CA62BA6F8C9F8DE770260AFE47F4E0F82F102198EBA27F543252AC8DDD83E1B8DB0A91AC65633FD12A550EBE96F93AA6704ED5905C234FA6D9203910CBD02DE166C4C3348FB81EF7B84AE1455FE318B5FD170883F49BA2F24289C479A2C7531406BA989BEAEF3A79F659028642E9B033F7DEB9ECEC3A7A9F1DBD2451FCB47C81E21E91D20B924C6BD04C1F0B2710D2E570CD24BAD5B5DE4E49AA80B6ADD5507B4D2E510370C7AFA814D7E1A7E278E53D7CCF49A0A866CA3A7B5BB71EF3425E460FEEB29149F217066613695F85506A0946CF68979F04AE073AF8028976BF0C5BDC2212E8C364583DE9FBD03B34DDEE5EC4CFA8ED8CE592971D0108FAF76C8940E25E6C5F865584C34A233C14F00532673FDBE388CC7E98A5B867B1C591307A9015112B567FF6B4F318114111FC95E5BD7C9C60B74C1F8725"); end:; } /* * SHAKE256 */ static const char *run_shake256(const char *hex) { return run_variable(hex, digest_SHAKE256()); } static void test_shake256(void *ptr) { /* KeccakCodePackage: ShortMsgKAT_SHAKE256.txt */ stri_check(run_shake256(""), "46B9DD2B0BA88D13233B3FEB743EEB243FCD52EA62B81B82B50C27646ED5762FD75DC4DDD8C0F200CB05019D67B592F6FC821C49479AB48640292EACB3B7C4BE141E96616FB13957692CC7EDD0B45AE3DC07223C8E92937BEF84BC0EAB862853349EC75546F58FB7C2775C38462C5010D846C185C15111E595522A6BCD16CF86F3D122109E3B1FDD943B6AEC468A2D621A7C06C6A957C62B54DAFC3BE87567D677231395F6147293B68CEAB7A9E0C58D864E8EFDE4E1B9A46CBE854713672F5CAAAE314ED9083DAB4B099F8E300F01B8650F1F4B1D8FCF3F3CB53FB8E9EB2EA203BDC970F50AE55428A91F7F53AC266B28419C3778A15FD248D339EDE785FB7F5A1AAA96D313EACC890936C173CDCD0FAB882C45755FEB3AED96D477FF96390BF9A66D1368B208E21F7C10D04A3DBD4E360633E5DB4B602601C14CEA737DB3DCF722632CC77851CBDDE2AAF0A33A07B373445DF490CC8FC1E4160FF118378F11F0477DE055A81A9EDA57A4A2CFB0C83929D310912F729EC6CFA36C6AC6A75837143045D791CC85EFF5B21932F23861BCF23A52B5DA67EAF7BAAE0F5FB1369DB78F3AC45F8C4AC5671D85735CDDDB09D2B1E34A1FC066FF4A162CB263D6541274AE2FCC865F618ABE27C124CD8B074CCD516301B91875824D09958F341EF274BDAB0BAE316339894304E35877B0C28A9B1FD166C796B9CC258A064A8F57E27F2A"); stri_check(run_shake256("CC"), "DDBF55DBF65977E3E2A3674D33E479F78163D592666BC576FEB5E4C404EA5E5329C3A416BE758687DE1A55E23D9E48A7D3F3CE6D8F0B2006A935800ECA9C9FC903D86F065367221067658B4D7473ED54800D196FBE1089811DD9B47F21E3698B1573653ADAD231C39F145B586D6C0133378416138E4423F7AF7DACFFE965706A3C49024002B53BA05871E3F066694B163630B0053BE41FA45D4317EAA84214254038A37A9C83D62EB9EA6E6ACAFA6BBFE5AF9F389690D5A9E27BF97C1B93D93ECF36DF6DA212E12B2448154156E94687336B6DA91E368512B9F34C616166542923F3618640D930F922A3DDDD2F87920378541446F2223F593931BD1BA02E2907CE3404621F26B900D05A1119A9E4934A7CD818DD9237445BF50475A011EA0078788801D21DFECB7A2BB294E4956DFA71D8CC472405342BF80120FE18A551D88D6ABC24D83F077BFB25EBDE5F4B03678D677EE646DCE3496D5138BE108782CA5A00AAFF3CB4BB873EC0F2E932DD74847033EC5F07254E3027B0AC12DB7D6D3F90B53D8A5BD63B99C3BF5CD38B453D7CB12D0AE2BF1CFD3EE88AF71BB6606B0B791DEFC2D762C8641BB8319FE17321EBA407EB744699D92B35ABD79F5B9A85408C93D61233FECE638023875AA02B9EDBACC84003A28CCA2D55A0742D635FDA888905986CA01C1E6420D49EC25224D84E915DFD1638A492282F1FD053770168953C"); stri_check(run_shake256("41FB"), "B64ECACD5F7499ACC085C908D35DCC1FC0131816F28D360592E1265079F92A5F844C4BF6AA50D98D52720797E8C992F43C76A73FD95F9BC4CD272157842ADA2518190FCA342DC20D0C57CDDF01B3DDF77977EDED63445E40BE82DF8D26DB629A2D307EE9FE28D2FE557E3971858C6D67C42BE2CF44DD7570521CE06474467425B7AAAE39DB90945BAD388009ED5715C684BB4E4981EEA324ECF66584AD08D9F27C6A4DCF615591857BC7364E8A7C136661AE5FFE828C734DD5EA5A071276E8477B8525E02B7B445D91CC6E37D58740DC2B069BE6D92E7DF95C1AB52B76F7761AE34328962EAC7156E460B3C04FFECAEC8722A56E7373285E42D4CAC5498F8D7DD5ECDA9F9973A32F8D425171E1390BFC812C9EE4D4AB8FA9A0D93AA90A4C258FC64D77BBCF49977E87C3810C80C4585168996A31F446F9391A193B888CD321E22E9368F4F11495FE124141C04015532345D7CB0A13A4DD9007D737B3A176A88E5FC153D4AC2E8CD641C40C4261BBA70E1B87114030FF67CB22ACEC90AC288D6B59D25B00038468B4780254FAC4EF158EC2CD52C0AB9217EED1ED0A5E7B4C4B3A64B1824E2B27AA53398765D5352BD1ED0E9C7B3FB264D141741659F7D8FD0EEEC9F9163C42AFDB540D5F2C87833880A0C942AE4CCEA7FFF2F4C798B8AAF24C33BE8054A09459A3AF7200D555334241709A18ECF88CE93C99234D6AB0285916AE"); stri_check(run_shake256("C1ECFDFC"), "CE7FBC15503986E3B84530D84A16EF64332A6EA57E354E9F2054BFC2AA8891F94B4FDD42FD227C94B33F9AD45CF3982239F81FBC2CBAC7809F01E1C294073AD6D2821C1283C5C427F41FD46455EF7B4EA0D6D5E249AF95FAC80A8678C1A5318443E63122DDFED2591FC690527F97A09920941B02439AF3BA36B17FD588E187FCBC1FF109AB4A9ECFC671926EF0CC9C82EE06EC6759E2758A8859B8FA9DDF46D6C049621FF589F3FF56C9419D6F48A68B68FEFD068ABEC24824D7FC150277C2439BF78D15D59DBAA2CB17E5282E6E9ED744841F4A4BBB778CFEAB0471CE850B2A2F948DB3926F733EF7B3AA9AB8EA84278DCA62B0691F5DD13EA11660A8E2FB5CD8BA74A352C044E7219E750F29B62F94ED268A68029B94B64F3D4F2193A7FC69ED34A59657471F04C4961084EBB581ABCC9D50E3674A7DEBB285FC7215BF6606DB1392A7F1619B347A4D07D230DA07811FDE2AEBE45A70178CF841C619F712EF26BEEECC4183A1040076E73FCF6FABE5C25A4B71D564A97CF3C88F1856D9C8B42E94F746CE4605D2AAEB56D1DA5527598E17E5680D309788E09910BEB74DF7D3C3CD4EC680083F5845F4A1C15070E57979C01B89DF7BE64435EA4817BC0AD23ACCA6CB878F7131F02F9215E2D5C12CF3B4D24B29620C891A54AC8BE6E3DEC08397887DE0EA86B81A5459B968FBAE18B4B0324DE7E7C1AEEFC7598644CE1FF8F9"); /* 1024 */ stri_check(run_shake256("2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817"), "E38785ED93686FA741FBB6E5BE64933963C3C872F7A4E8C8D540EC3F82284605625D32A24BCE8B40264EB51B164DD86F318ACFD9867F3BF23998275042AAF23BDA01F602622448957B81E51475F15CDB31A9297EE390F681E460EC01A024F183110C728BB09A12DDE89E6F5DE2B4F8C17D981E3E9C531E6D4C19448A4A6BE28853AFA2FBA216F7C8E2CE4E4DE31F6B0E129CB5DA118B4A59569A439C1095EB50C51CD83105B1A12B3F7086947EA7381969A78308F8FDDAC8B2D87F4540A8E7CAC8932CA76201F86561ADD09D833361851CEB9759AD1EEED4E00ED19C642BC6D0AED034276B66D818E8791A7C1F42F8604E8B026D4635E3BDB27CA0FB28E7517BF662BB99AE3A3C698AD0AA2F02408E76A5F93ABFC933B1691A89EE3EBCA2885EA633D0FEA4DBCD03B05B68E0F4D267144FDC0898DE46011832ADC872F4A7F0D8933CDD482CA54FFA81774BA083D3D9FE07DE94F8E03FF66D9F1BFE7504E8A497591BA8F52758F92E0A4CA6A93979CD1F55EE9DBA17BAC6C69E83DDED4CE2DBFFB0B48C0C6AED657405DE18E7891B3C854127459E89FE5442CA6D5C223B046147BFDEE435CF4EFAEFD705373DC87E20B7D31C7D37907D30B8B32054BB9ECA80D0AFABB5EC5053D94D517F158EA958C7458CF71FCA85B607A352A9A3B8F4F161E3B8853F6BB61864BAD12DD9B57ECDA507A3CAA63A1DEC726B518CB4FDDEED6A34"); /* 2040 */ stri_check(run_shake256("3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1"), "8A5199B4A7E133E264A86202720655894D48CFF344A928CF8347F48379CEF347DFC5BCFFAB99B27B1F89AA2735E23D30088FFA03B9EDB02B9635470AB9F1038985D55F9CA774572DD006470EA65145469609F9FA0831BF1FFD842DC24ACADE27BD9816E3B5BF2876CB112232A0EB4475F1DFF9F5C713D9FFD4CCB89AE5607FE35731DF06317949EEF646E9591CF3BE53ADD6B7DD2B6096E2B3FB06E662EC8B2D77422DAAD9463CD155204ACDBD38E319613F39F99B6DFB35CA9365160066DB19835888C2241FF9A731A4ACBB5663727AAC34A401247FBAA7499E7D5EE5B69D31025E63D04C35C798BCA1262D5673A9CF0930B5AD89BD485599DC184528DA4790F088EBD170B635D9581632D2FF90DB79665CED430089AF13C9F21F6D443A818064F17AEC9E9C5457001FA8DC6AFBADBE3138F388D89D0E6F22F66671255B210754ED63D81DCE75CE8F189B534E6D6B3539AA51E837C42DF9DF59C71E6171CD4902FE1BDC73FB1775B5C754A1ED4EA7F3105FC543EE0418DAD256F3F6118EA77114A16C15355B42877A1DB2A7DF0E155AE1D8670ABCEC3450F4E2EEC9838F895423EF63D261138BAAF5D9F104CB5A957AEA06C0B9B8C78B0D441796DC0350DDEABB78A33B6F1F9E68EDE3D1805C7B7E2CFD54E0FAD62F0D8CA67A775DC4546AF9096F2EDB221DB42843D65327861282DC946A0BA01A11863AB2D1DFD16E3973D4"); end:; } /* * HMAC */ static const char *run_hmac(const char *key, const char *str, const struct DigestInfo *impl) { struct HMAC *ctx; uint8_t res[512]; int len = strlen(str); int reslen; ctx = hmac_new(impl, key, strlen(key), NULL); if (!ctx) return "NOMEM"; reslen = hmac_result_len(ctx); hmac_update(ctx, str, len); hmac_final(ctx, res); hmac_free(ctx); return mkhex(res, reslen); } static const char *run_hmac_sha1(const char *key, const char *str) { return run_hmac(key, str, digest_SHA1()); } static void test_hmac(void *ptr) { const char *long_key = ( "quite a very long key, longer than a sha1 block size, " "so it needs to be sha-1d before being used as a key"); const char *text = "The quick brown fox jumps over the lazy dog"; str_check(run_hmac_sha1("", ""), "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d"); str_check(run_hmac_sha1("shrt", ""), "41fee95de96c437cf6c2f38363eb38eb0067ff64"); str_check(run_hmac_sha1(long_key, ""), "496ca9bda3e523814ba7f99f68a2035e4de7702a"); str_check(run_hmac_sha1(long_key, text), "924e1ee84da31f5f569a27dd6201533b42c999c6"); str_check(run_hmac_sha1("key", text), "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"); end:; } /* * keccak_prng */ static void test_keccak_prng(void *z) { struct KeccakPRNG state; const char *ent = "The quick brown fox jumps over the lazy dog."; const char *ent2 = "More entropy."; uint8_t buf[32]; int i; tt_assert(keccak_prng_init(&state, 1) == false); tt_assert(keccak_prng_init(&state, 1024) == true); tt_assert(keccak_prng_extract(&state, buf, 32) == false); keccak_prng_add_data(&state, ent, strlen(ent)); tt_assert(keccak_prng_extract(&state, buf, 32) == true); str_check(mkhex(buf, 32), "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded638788"); if (1) { tt_assert(keccak_prng_extract(&state, buf, 32) == true); } else { for (i = 0; i < 32; i++) { tt_assert(keccak_prng_extract(&state, buf+i, 1) == true); } } str_check(mkhex(buf, 32), "9064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760"); tt_assert(keccak_prng_extract(&state, buf, 32) == true); str_check(mkhex(buf, 32), "7ca34f68abb61bbd1821c0a499599426031a56c495b3cf91b84cacafb9be816b"); tt_assert(keccak_prng_extract(&state, buf, 32) == true); str_check(mkhex(buf, 32), "e7afb50b3a1c80f654ba212be0ad8a4be8f6a476bfcc66b9401fe65924bd547d"); keccak_prng_add_data(&state, ent2, strlen(ent2)); tt_assert(keccak_prng_extract(&state, buf, 32) == true); str_check(mkhex(buf, 32), "ec9f73358469f4b7fea10dfb7dfaa768f573089b8e00507ec3a1fdfb2e60b35d"); end:; } /* * chacha. */ static const char *run_chacha(const char *key, const char *iv, uint32_t c1, uint32_t c2) { int klen = strlen(key) / 2; void *kb, *ivb; struct ChaCha ctx; uint8_t output[128]; if (klen != 32 && klen != 16) return "KeyError"; if (strlen(iv) != 8*2) return "IvError"; kb = fromhex(key, klen); if (klen == 32) { chacha_set_key_256(&ctx, kb); } else { chacha_set_key_128(&ctx, kb); } free(kb); ivb = fromhex(iv, 8); chacha_set_nonce(&ctx, c1, c2, ivb); free(ivb); if (1) { static unsigned int blkver = 0; static const int blklens[] = {128, 1, 7, 11, 66}; int blk = blklens[blkver++]; int n, need = sizeof(output); uint8_t *dst = output; if (blkver >= ARRAY_NELEM(blklens)) blkver = 0; while (need > 0) { n = (need > blk) ? blk : need; chacha_keystream(&ctx, dst, n); dst += n; need -= n; } } else { chacha_keystream(&ctx, output, sizeof(output)); } return mkhex(output, sizeof(output)); } /* https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01 */ static void test_chacha(void *z) { /* TC1: All zero key and IV. */ str_check(run_chacha("00000000000000000000000000000000", "0000000000000000", 0, 0), "89670952608364fd00b2f90936f031c8e756e15dba04b8493d00429259b20f46cc04f111246b6c2ce066be3bfb32d9aa0fddfbc12123d4b9e44f34dca05a103f" "6cd135c2878c832b5896b134f6142a9d4d8d0d8f1026d20a0a81512cbce6e9758a7143d021978022a384141a80cea3062f41f67a752e66ad3411984c787e30ad"); str_check(run_chacha("0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000", 0, 0), "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586" "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f"); /* TC2: Single bit in key set. All zero IV. */ str_check(run_chacha("01000000000000000000000000000000", "0000000000000000", 0, 0), "ae56060d04f5b597897ff2af1388dbceff5a2a4920335dc17a3cb1b1b10fbe70ece8f4864d8c7cdf0076453a8291c7dbeb3aa9c9d10e8ca36be4449376ed7c42" "fc3d471c34a36fbbf616bc0a0e7c523030d944f43ec3e78dd6a12466547cb4f7b3cebd0a5005e762e562d1375b7ac44593a991b85d1a60fba2035dfaa2a642d5"); str_check(run_chacha("0100000000000000000000000000000000000000000000000000000000000000", "0000000000000000", 0, 0), "c5d30a7ce1ec119378c84f487d775a8542f13ece238a9455e8229e888de85bbd29eb63d0a17a5b999b52da22be4023eb07620a54f6fa6ad8737b71eb0464dac0" "10f656e6d1fd55053e50c4875c9930a33f6d0263bd14dfd6ab8c70521c19338b2308b95cf8d0bb7d202d2102780ea3528f1cb48560f76b20f382b942500fceac"); /* TC3: Single bit in IV set. All zero key. */ str_check(run_chacha("00000000000000000000000000000000", "0100000000000000", 0, 0), "1663879eb3f2c9949e2388caa343d361bb132771245ae6d027ca9cb010dc1fa7178dc41f8278bc1f64b3f12769a24097f40d63a86366bdb36ac08abe60c07fe8" "b057375c89144408cc744624f69f7f4ccbd93366c92fc4dfcada65f1b959d8c64dfc50de711fb46416c2553cc60f21bbfd006491cb17888b4fb3521c4fdd8745"); str_check(run_chacha("0000000000000000000000000000000000000000000000000000000000000000", "0100000000000000", 0, 0), "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b" "5305e5e44aff19b235936144675efbe4409eb7e8e5f1430f5f5836aeb49bb5328b017c4b9dc11f8a03863fa803dc71d5726b2b6b31aa32708afe5af1d6b69058"); /* TC4: All bits in key and IV are set. */ str_check(run_chacha("ffffffffffffffffffffffffffffffff", "ffffffffffffffff", 0, 0), "992947c3966126a0e660a3e95db048de091fb9e0185b1e41e41015bb7ee50150399e4760b262f9d53f26d8dd19e56f5c506ae0c3619fa67fb0c408106d0203ee" "40ea3cfa61fa32a2fda8d1238a2135d9d4178775240f99007064a6a7f0c731b67c227c52ef796b6bed9f9059ba0614bcf6dd6e38917f3b150e576375be50ed67"); str_check(run_chacha("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffff", 0, 0), "d9bf3f6bce6ed0b54254557767fb57443dd4778911b606055c39cc25e674b8363feabc57fde54f790c52c8ae43240b79d49042b777bfd6cb80e931270b7f50eb" "5bac2acd86a836c5dc98c116c1217ec31d3a63a9451319f097f3b4d6dab0778719477d24d24b403a12241d7cca064f790f1d51ccaff6b1667d4bbca1958c4306"); /* TC5: Every even bit set in key and IV. */ str_check(run_chacha("55555555555555555555555555555555", "5555555555555555", 0, 0), "357d7d94f966778f5815a2051dcb04133b26b0ead9f57dd09927837bc3067e4b6bf299ad81f7f50c8da83c7810bfc17bb6f4813ab6c326957045fd3fd5e19915" "ec744a6b9bf8cbdcb36d8b6a5499c68a08ef7be6cc1e93f2f5bcd2cad4e47c18a3e5d94b5666382c6d130d822dd56aacb0f8195278e7b292495f09868ddf12cc"); str_check(run_chacha("5555555555555555555555555555555555555555555555555555555555555555", "5555555555555555", 0, 0), "bea9411aa453c5434a5ae8c92862f564396855a9ea6e22d6d3b50ae1b3663311a4a3606c671d605ce16c3aece8e61ea145c59775017bee2fa6f88afc758069f7" "e0b8f676e644216f4d2a3422d7fa36c6c4931aca950e9da42788e6d0b6d1cd838ef652e97b145b14871eae6c6804c7004db5ac2fce4c68c726d004b10fcaba86"); /* TC6: Every odd bit set in key and IV. */ str_check(run_chacha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", 0, 0), "fc79acbd58526103862776aab20f3b7d8d3149b2fab65766299316b6e5b16684de5de548c1b7d083efd9e3052319e0c6254141da04a6586df800f64d46b01c87" "1f05bc67e07628ebe6f6865a2177e0b66a558aa7cc1e8ff1a98d27f7071f8335efce4537bb0ef7b573b32f32765f29007da53bba62e7a44d006f41eb28fe15d6"); str_check(run_chacha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", 0, 0), "9aa2a9f656efde5aa7591c5fed4b35aea2895dec7cb4543b9e9f21f5e7bcbcf3c43c748a970888f8248393a09d43e0b7e164bc4d0b0fb240a2d72115c4808906" "72184489440545d021d97ef6b693dfe5b2c132d47e6f041c9063651f96b623e62a11999a23b6f7c461b2153026ad5e866a2e597ed07b8401dec63a0934c6b2a9"); /* TC7: Sequence patterns in key and IV. */ str_check(run_chacha("00112233445566778899aabbccddeeff", "0f1e2d3c4b5a6978", 0, 0), "d1abf630467eb4f67f1cfb47cd626aae8afedbbe4ff8fc5fe9cfae307e74ed451f1404425ad2b54569d5f18148939971abb8fafc88ce4ac7fe1c3d1f7a1eb7ca" "e76ca87b61a9713541497760dd9ae059350cad0dcedfaa80a883119a1a6f987fd1ce91fd8ee0828034b411200a9745a285554475d12afc04887fef3516d12a2c"); str_check(run_chacha("00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100", "0f1e2d3c4b5a6978", 0, 0), "9fadf409c00811d00431d67efbd88fba59218d5d6708b1d685863fabbb0e961eea480fd6fb532bfd494b2151015057423ab60a63fe4f55f7a212e2167ccab931" "fbfd29cf7bc1d279eddf25dd316bb8843d6edee0bd1ef121d12fa17cbc2c574cccab5e275167b08bd686f8a09df87ec3ffb35361b94ebfa13fec0e4889d18da5"); /* TC8: Random Key */ str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb735", "1ada31d5cf688221", 0, 0), "826abdd84460e2e9349f0ef4af5b179b426e4b2d109a9c5bb44000ae51bea90a496beeef62a76850ff3f0402c4ddc99f6db07f151c1c0dfac2e56565d6289625" "5b23132e7b469c7bfb88fa95d44ca5ae3e45e848a4108e98bad7a9eb15512784a6a9e6e591dce674120acaf9040ff50ff3ac30ccfb5e14204f5e4268b90a8804"); str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", "1ada31d5cf688221", 0, 0), "f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473afc25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92" "e5fbc34e60a1d9a9db17345b0a402736853bf910b060bdf1f897b6290f01d138ae2c4c90225ba9ea14d518f55929dea098ca7a6ccfe61227053c84e49a4a3332"); /* Counter overflow */ str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb735", "1ada31d5cf688221", 0xFFFFFFFF, 0), "9220e40c9fa71e1e210b216b5f1c829e73d31c6891447084d5c5bc29db812ec2f4c64a594214a3437cb04548f9a1c4839f03405f1e2b5db69d9df11474a4610c" "60f15b0e01460b61d8ac73cac1de084c741c157fb75d52719ed98a62bcada8187041035178a1845b164f0d82b4fc20a6ad1668d3177fac688cb08d3df75281fe"); str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", "1ada31d5cf688221", 0xFFFFFFFF, 0xFFFFFFFF), "489b569d1a0649a3185f04dfda7cbb688503f3485ea0754e7a9c17452e0f6a123a1e24d4313d79c9bf7c4fd714211dca39c1717f29b7d137158b7f620bdcb759" "f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473afc25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92"); end:; } /* * csrandom. */ static uint32_t calc_lim(uint32_t val) { uint32_t mod, lim; /* mod = 2**32 % x = (2**32 - x) % x */ mod = -val % val; /* wait for value in range [0 .. 2**32 - mod ) */ lim = -mod; return lim; } static void test_csrandom(void *z) { uint32_t half, v, lim, i, v2; half = 1 << 31; for (v = 2; v < 1024; v++) { lim = calc_lim(v); int_check(lim % v, 0); tt_assert(lim >= v || lim == 0); } for (v = half - 1024; v < half + 1024; v++) { lim = calc_lim(v); int_check(lim % v, 0); tt_assert(lim >= v || lim == 0); } for (v = 0xFFFFFF00U; v != 0; v++) { lim = calc_lim(v); int_check(lim % v, 0); tt_assert(lim >= v); } lim = 0; v = csrandom(); for (i = 0; i < 100; i++) { v2 = csrandom(); if (v == v2) lim++; v = v2; } tt_assert(lim < 10); end:; } /* * Launcher. */ struct testcase_t crypto_tests[] = { { "md5", test_md5 }, { "sha1", test_sha1 }, { "sha224", test_sha224 }, { "sha256", test_sha256 }, { "sha384", test_sha384 }, { "sha512", test_sha512 }, { "sha3-224", test_sha3_224 }, { "ska3-256", test_sha3_256 }, { "sha3-384", test_sha3_384 }, { "sha3-512", test_sha3_512 }, { "shake128", test_shake128 }, { "shake256", test_shake256 }, { "hmac", test_hmac }, { "keccak_prng", test_keccak_prng }, { "chacha", test_chacha }, { "csrandom", test_csrandom }, END_OF_TESTCASES }; pgqd/lib/test/tinytest_macros.h0000664000401600040160000001207213175113172015203 0ustar cbecbe/* tinytest_macros.h -- Copyright 2009 Nick Mathewson * * 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. */ #ifndef _TINYTEST_MACROS_H #define _TINYTEST_MACROS_H /* Helpers for defining statement-like macros */ #define TT_STMT_BEGIN do { #define TT_STMT_END } while(0) /* Redefine this if your test functions want to abort with something besides * "goto end;" */ #ifndef TT_EXIT_TEST_FUNCTION #define TT_EXIT_TEST_FUNCTION TT_STMT_BEGIN goto end; TT_STMT_END #endif /* Redefine this if you want to note success/failure in some different way. */ #ifndef TT_DECLARE #define TT_DECLARE(prefix, args) \ TT_STMT_BEGIN \ printf("\n %s %s:%d: ",prefix,__FILE__,__LINE__); \ printf args ; \ TT_STMT_END #endif /* Announce a failure. Args are parenthesized printf args. */ #define TT_GRIPE(args) TT_DECLARE("FAIL", args) /* Announce a non-failure if we're verbose. */ #define TT_BLATHER(args) \ TT_STMT_BEGIN \ if (_tinytest_get_verbosity()>1) TT_DECLARE(" OK", args); \ TT_STMT_END #define TT_DIE(args) \ TT_STMT_BEGIN \ _tinytest_set_test_failed(); \ TT_GRIPE(args); \ TT_EXIT_TEST_FUNCTION; \ TT_STMT_END #define TT_FAIL(args) \ TT_STMT_BEGIN \ _tinytest_set_test_failed(); \ TT_GRIPE(args); \ TT_STMT_END /* Fail and abort the current test for the reason in msg */ #define tt_abort_printf(msg) TT_DIE(msg) #define tt_abort_perror(op) TT_DIE(("%s: %s [%d]",(op),strerror(errno), errno)) #define tt_abort_msg(msg) TT_DIE(("%s", msg)) #define tt_abort() TT_DIE(("%s", "(Failed.)")) /* Fail but do not abort the current test for the reason in msg. */ #define tt_fail_printf(msg) TT_FAIL(msg) #define tt_fail_perror(op) TT_FAIL(("%s: %s [%d]",(op),strerror(errno), errno)) #define tt_fail_msg(msg) TT_FAIL(("%s", msg)) #define tt_fail() TT_FAIL(("%s", "(Failed.)")) /* End the current test, and indicate we are skipping it. */ #define tt_skip() \ TT_STMT_BEGIN \ _tinytest_set_test_skipped(); \ TT_EXIT_TEST_FUNCTION; \ TT_STMT_END #define _tt_want(b, msg, fail) \ TT_STMT_BEGIN \ if (!(b)) { \ _tinytest_set_test_failed(); \ TT_GRIPE((msg)); \ fail; \ } else { \ TT_BLATHER((msg)); \ } \ TT_STMT_END /* Assert b, but do not stop the test if b fails. Log msg on failure. */ #define tt_want_msg(b, msg) \ _tt_want(b, msg, ); /* Assert b and stop the test if b fails. Log msg on failure. */ #define tt_assert_msg(b, msg) \ _tt_want(b, msg, TT_EXIT_TEST_FUNCTION); /* Assert b, but do not stop the test if b fails. */ #define tt_want(b) tt_want_msg( (b), "want("#b")") /* Assert b, and stop the test if b fails. */ #define tt_assert(b) tt_assert_msg((b), "assert("#b")") #define tt_assert_test_type(a,b,str_test,type,test,fmt) \ TT_STMT_BEGIN \ type _val1 = (type)(a); \ type _val2 = (type)(b); \ if (!(test)) { \ TT_DIE(("assert(%s): "fmt" vs "fmt, \ str_test, _val1, _val2)); \ } else { \ TT_BLATHER(("assert(%s): "fmt" vs "fmt, \ str_test, _val1, _val2)); \ } \ TT_STMT_END /* Helper: assert that a op b, when cast to type. Format the values with * printf format fmt on failure. */ #define tt_assert_op_type(a,op,b,type,fmt) \ tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt) #define tt_int_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld") #define tt_uint_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \ (_val1 op _val2),"%lu") #define tt_ptr_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,void*, \ (_val1 op _val2),"%p") #define tt_str_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \ (strcmp(_val1,_val2) op 0),"<%s>") #endif pgqd/lib/test/test_fnmatch.c0000664000401600040160000001536613175113172014437 0ustar cbecbe #include #include #include #include "test_common.h" /* * POSIX syntax. */ static void test_fnmatch_posix(void *p) { /* literal */ int_check(0, fnmatch("", "", 0)); int_check(0, fnmatch("a", "a", 0)); int_check(0, fnmatch("abc", "abc", 0)); int_check(1, fnmatch("", "b", 0)); int_check(1, fnmatch("a", "", 0)); int_check(1, fnmatch("a", "b", 0)); /* single wildcard */ int_check(0, fnmatch("a?", "ax", 0)); int_check(0, fnmatch("??", "ax", 0)); int_check(1, fnmatch("?", "ax", 0)); int_check(1, fnmatch("???", "ax", 0)); /* wildcard */ int_check(0, fnmatch("a*", "ax", 0)); int_check(0, fnmatch("*", "", 0)); int_check(0, fnmatch("*", "qwe", 0)); int_check(0, fnmatch("ab*ab", "abxxab", 0)); int_check(1, fnmatch("ab*ab", "abxxabc", 0)); /* wildcard+ */ int_check(0, fnmatch("ab*ab*", "abxxabc", 0)); int_check(0, fnmatch("ab*ab*c", "abxxabc", 0)); int_check(0, fnmatch("ab*ab*c", "abxxabxc", 0)); int_check(0, fnmatch("??*??", "abxxab", 0)); int_check(0, fnmatch("*??", "abxxab", 0)); int_check(0, fnmatch("??*", "abxxab", 0)); int_check(0, fnmatch("a**c", "abc", 0)); /* classes */ int_check(0, fnmatch("[abc]", "b", 0)); int_check(1, fnmatch("[abc]", "x", 0)); int_check(0, fnmatch("[a-c]", "b", 0)); int_check(1, fnmatch("[a-c]", "x", 0)); int_check(0, fnmatch("[b-b]", "b", 0)); int_check(1, fnmatch("[!abc]", "b", 0)); int_check(1, fnmatch("[!a-c]", "b", 0)); int_check(0, fnmatch("[!a-c]", "x", 0)); int_check(0, fnmatch("[*?[][*?[][*?[]", "*?[", 0)); int_check(0, fnmatch("[[:alpha:]][![:alpha:]]", "a9", 0)); int_check(0, fnmatch("[[:alnum:]][![:alnum:]]", "9-", 0)); #ifdef iswblank int_check(0, fnmatch("[[:blank:]][![:blank:]]", " -", 0)); #endif int_check(0, fnmatch("[[:cntrl:]][![:cntrl:]]", "\tx", 0)); int_check(0, fnmatch("[[:digit:]][![:digit:]]", "9a", 0)); int_check(0, fnmatch("[[:graph:]][![:graph:]]", "a\t", 0)); int_check(0, fnmatch("[[:lower:]][![:lower:]]", "aA", 0)); int_check(0, fnmatch("[[:print:]][![:print:]]", "a\n", 0)); int_check(0, fnmatch("[[:punct:]][![:punct:]]", ".x", 0)); int_check(0, fnmatch("[[:space:]][![:space:]]", " x", 0)); int_check(0, fnmatch("[[:upper:]][![:upper:]]", "Ff", 0)); int_check(0, fnmatch("[[:xdigit:]][![:xdigit:]]", "Fx", 0)); int_check(0, fnmatch("[", "[", 0)); int_check(0, fnmatch("[f", "[f", 0)); /* escaping */ int_check(1, fnmatch("\\a\\?", "ax", 0)); int_check(0, fnmatch("\\a\\?", "a?", 0)); int_check(1, fnmatch("\\a\\*", "ax", 0)); int_check(0, fnmatch("\\a\\*", "a*", 0)); int_check(0, fnmatch("\\[a]", "[a]", 0)); int_check(0, fnmatch("\\\\", "\\", 0)); int_check(0, fnmatch("\\$\\'\\\"\\<\\>", "$'\"<>", 0)); int_check(1, fnmatch("a\\", "a", 0)); int_check(1, fnmatch("a\\", "a\\", 0)); int_check(0, fnmatch("a\\", "a\\", FNM_NOESCAPE)); int_check(0, fnmatch("\\[a]", "\\a", FNM_NOESCAPE)); int_check(0, fnmatch("\\*b", "\\aab", FNM_NOESCAPE)); /* FNM_PATHNAME */ int_check(0, fnmatch("ab*c", "ab/c", 0)); int_check(1, fnmatch("ab*c", "ab/c", FNM_PATHNAME)); int_check(1, fnmatch("ab?c", "ab/c", FNM_PATHNAME)); int_check(1, fnmatch("ab[/]c", "ab/c", FNM_PATHNAME)); int_check(0, fnmatch("/*/", "//", FNM_PATHNAME)); int_check(1, fnmatch("a[b/c]d", "a/d", FNM_PATHNAME)); int_check(0, fnmatch("abd", "abd", FNM_PATHNAME)); int_check(1, fnmatch("a[b/c]d", "a[b/c]d", FNM_PATHNAME)); /* FNM_PERIOD */ int_check(0, fnmatch(".foo", ".foo", 0)); int_check(0, fnmatch("?foo", ".foo", 0)); int_check(0, fnmatch("[.]foo", ".foo", 0)); int_check(0, fnmatch("[!abc]foo", ".foo", 0)); int_check(0, fnmatch("*foo", ".foo", 0)); int_check(0, fnmatch(".foo", ".foo", FNM_PERIOD)); int_check(1, fnmatch("*foo", ".foo", FNM_PERIOD)); int_check(1, fnmatch("?foo", ".foo", FNM_PERIOD)); int_check(0, fnmatch("*/?foo", "sub/.foo", FNM_PERIOD)); int_check(1, fnmatch("*.foo", ".foo", FNM_PERIOD)); int_check(1, fnmatch("[.]foo", ".foo", FNM_PERIOD)); /* FNM_PATHNAME | FNM_PERIOD */ int_check(1, fnmatch("*/?foo", "sub/.foo", FNM_PERIOD|FNM_PATHNAME)); int_check(1, fnmatch("*/[.]foo", "sub/.foo", FNM_PERIOD|FNM_PATHNAME)); int_check(1, fnmatch("*/*.c", "sub/.foo.c", FNM_PERIOD|FNM_PATHNAME)); int_check(1, fnmatch("*/*", "sub/.foo.c", FNM_PERIOD|FNM_PATHNAME)); int_check(0, fnmatch("*/*.c", "sub/foo..c", FNM_PERIOD|FNM_PATHNAME)); int_check(1, fnmatch("*/*.foo", "sub/.foo", FNM_PERIOD|FNM_PATHNAME)); /* escapes in brackets ~ posix */ int_check(0, fnmatch("[A\\]]", "\\]", FNM_NOESCAPE)); #ifndef HAVE_FNMATCH int_check(0, fnmatch("[a\\-x]", "_", FNM_NOESCAPE)); #endif end:; } /* * GNU syntax. */ static void test_fnmatch_gnu(void *p) { /* FNM_CASEFOLD */ int_check(1, fnmatch("aaAA", "AaAa", 0)); int_check(1, fnmatch("[b][b][B][B][a-c][A-C][a-c][A-C]", "bBbBbbBB", 0)); int_check(0, fnmatch("aaAA", "AaAa", FNM_CASEFOLD)); int_check(0, fnmatch("[b][b][B][B][a-c][A-C][a-c][A-C]", "bBbBbbBB", FNM_CASEFOLD)); /* FNM_LEADING_DIR */ int_check(0, fnmatch("a", "a", FNM_LEADING_DIR|FNM_PATHNAME)); int_check(0, fnmatch("a", "a/b", FNM_LEADING_DIR|FNM_PATHNAME)); int_check(0, fnmatch("a/b", "a/b/c/d", FNM_LEADING_DIR|FNM_PATHNAME)); int_check(0, fnmatch("a/*/*", "a/b/c/d", FNM_LEADING_DIR|FNM_PATHNAME)); int_check(0, fnmatch("*", "/a", FNM_LEADING_DIR|FNM_PATHNAME)); /* seems wrong to allow it */ int_check(0, fnmatch("a", "a/b", FNM_LEADING_DIR)); /* escapes in brackets ~ gnu */ int_check(0, fnmatch("[A\\]][A\\]]", "]A", 0)); int_check(1, fnmatch("[a\\-x]", "_", 0)); int_check(0, fnmatch("[\\!x]", "!", 0)); int_check(1, fnmatch("[\\!x]", "\\", 0)); int_check(0, fnmatch("[\\[:alnum:]", ":", 0)); end:; } /* * DoS possibilities. */ static void test_fnmatch_weird(void *p) { char pat[4096]; char str[4096]; int i; memset(pat, 0, sizeof(pat)); memset(str, 0, sizeof(str)); memset(pat, '*', 1500); memset(str, 'a', 1500); int_check(0, fnmatch(pat, str, 0)); pat[10] = 'a'; pat[1200] = 'b'; int_check(0, fnmatch(pat, "ab", 0)); for (i = 0; i < 1200; i++) { char c = 'a' + (i%26); pat[i*2] = c; pat[i*2+1] = '*'; str[i*2] = c; str[i*2+1] = c; } pat[i*2] = 0; str[i*2] = 0; int_check(0, fnmatch(pat, str, 0)); for (i = 0; i < 2000; i++) { pat[i*2] = '*'; pat[i*2+1] = '?'; str[i*2] = 'a'; str[i*2+1] = 'b'; } str[i*2] = 0; pat[i*2] = 0; int_check(0, fnmatch(pat, str, 0)); pat[i*2] = 'a'; pat[i*2 + 1] = 0; int_check(1, fnmatch(pat, str, 0)); pat[i*2] = 'b'; int_check(0, fnmatch(pat, str, 0)); pat[i*2] = '*'; pat[3] = 'x'; str[2000] = 'x'; int_check(0, fnmatch(pat, str, 0)); memset(pat, '?', sizeof(pat)); memset(str, 'x', sizeof(str)); str[4000] = 0; pat[2000] = 0; pat[0] = '*'; int_check(0, fnmatch(pat, str, 0)); end:; } struct testcase_t fnmatch_tests[] = { { "posix", test_fnmatch_posix }, { "gnu", test_fnmatch_gnu }, { "weird", test_fnmatch_weird }, END_OF_TESTCASES }; pgqd/lib/test/test_getopt.c0000664000401600040160000000433413175113172014312 0ustar cbecbe #include #include "test_common.h" #include #include static const char *xgetopt(const char *opts, const struct option *lopts, ...) { static char resbuf[1024]; int i, c, argc = 1; char *argv[100]; va_list ap; char *p = resbuf, *bufend = resbuf + sizeof resbuf; resbuf[0] = 'X'; resbuf[1] = 0; argv[0] = "prog"; va_start(ap, lopts); while (1) { argv[argc] = va_arg(ap, char *); if (!argv[argc]) break; argc++; } va_end(ap); opterr = 0; optind = 0; while (1) { if (lopts) c = getopt_long(argc, argv, opts, lopts, NULL); else c = getopt(argc, argv, opts); if (c == -1) break; switch (c) { case '?': return "ERR"; case ':': return "EARG"; case 0: break; default: if (p != resbuf) *p++ = ','; if (optarg) p += snprintf(p, bufend - p, "%c=%s", c, optarg); else p += snprintf(p, bufend - p, "%c", c); } } for (i = optind; i < argc; i++) p += snprintf(p, bufend - p, "|%s", argv[i]); if (p >= bufend) return "bufover"; return resbuf; } static void test_getopt(void *_) { str_check(xgetopt("ab:", NULL, "-abFOO", "zzz", NULL), "a,b=FOO|zzz"); str_check(xgetopt("ab:", NULL, "-a", "zzz", "-bFOO", NULL), "a,b=FOO|zzz"); str_check(xgetopt("ab:", NULL, "-b", "FOO", "-", "--", "-a", NULL), "b=FOO|-|-a"); str_check(xgetopt("ab:", NULL, "--foo", NULL), "ERR"); end:; } static void test_getopt_long(void *_) { static int longc; static const char sopts[] = "ab:"; static const struct option lopts[] = { { "longa", no_argument, NULL, 'a'}, { "longb", required_argument, NULL, 'b'}, { "longc", no_argument, &longc, 'C'}, { NULL }, }; str_check(xgetopt(sopts, lopts, "--longa", "--", "--longa", NULL), "a|--longa"); str_check(xgetopt(sopts, lopts, "--longb", "FOO", "ARG", "--longa", NULL), "b=FOO,a|ARG"); str_check(xgetopt(sopts, lopts, "--longb=BAZ", NULL), "b=BAZ"); str_check(xgetopt(sopts, lopts, "--longb", NULL), "ERR"); str_check(xgetopt(sopts, lopts, "--xx", NULL), "ERR"); str_check(xgetopt(sopts, lopts, "-", "--longc", "ARG", NULL), "|-|ARG"); tt_assert(longc == 'C'); end:; } struct testcase_t getopt_tests[] = { { "getopt", test_getopt }, { "getopt_long", test_getopt_long }, END_OF_TESTCASES }; pgqd/lib/test/test_mdict.c0000664000401600040160000000250213175113172014103 0ustar cbecbe#include #include #include "test_common.h" static const char *xget(struct MDict *d, const char *k) { const char *val = mdict_get(d, k); return val ? val : "NULL"; } static void test_mdict(void *p) { struct MDict *d; struct MBuf buf; const char *s; d = mdict_new(NULL); str_check(xget(d, "key"), "NULL"); int_check(mdict_put(d, "key", "val"), 1); int_check(mdict_put(d, "key2", "foo"), 1); int_check(mdict_put(d, "key2", ""), 1); int_check(mdict_put(d, "key3", NULL), 1); int_check(mdict_put(d, "key4", "v1"), 1); int_check(mdict_del(d, "key4"), 1); str_check(xget(d, "key"), "val"); str_check(xget(d, "key2"), ""); str_check(xget(d, "key3"), "NULL"); str_check(xget(d, "key4"), "NULL"); str_check(xget(d, "key5"), "NULL"); int_check(mdict_del(d, "key5"), 0); mbuf_init_dynamic(&buf); int_check(mdict_urlencode(d, &buf), 1); int_check(mbuf_write_byte(&buf, 0), 1); str_check(mbuf_data(&buf), "key=val&key2=&key3"); mbuf_free(&buf); mdict_free(d); d = mdict_new(NULL); s = "key=val&key2=&key3"; int_check(mdict_urldecode(d, s, strlen(s)), 1); str_check(xget(d, "key"), "val"); str_check(xget(d, "key2"), ""); str_check(xget(d, "key3"), "NULL"); mdict_free(d); end:; } /* * Describe */ struct testcase_t mdict_tests[] = { { "basic", test_mdict }, END_OF_TESTCASES }; pgqd/lib/test/test_tls.c0000664000401600040160000007101313175113172013610 0ustar cbecbe#include #include #include #include #include #include #include #include #include #include #include "test_common.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #endif #include enum WState { HANDSHAKE, CONNECTED, CLOSED }; struct Worker { struct event ev; struct event_base *evbase; struct tls *ctx; struct tls *base; struct tls_config *config; const char *hostname; bool is_server; bool pending; enum WState wstate; int socket; char errbuf[1024]; const char *show; char showbuf[1024]; const char *peer_fingerprint_sha1; const char *peer_fingerprint_sha256; int aggressive_close; }; static void free_worker(struct Worker *w) { if (!w) return; if (event_initialized(&w->ev)) event_del(&w->ev); tls_free(w->ctx); tls_free(w->base); tls_config_free(w->config); if (w->socket > 0) close(w->socket); memset(w, 0, sizeof *w); free(w); } _PRINTF(2,3) static void add_error(struct Worker *w, const char *s, ...) { char buf[1024]; va_list ap; va_start(ap, s); vsnprintf(buf, sizeof buf, s, ap); va_end(ap); s = buf; if (strcmp(s, "OK") == 0) return; if (strstr(s, "SSL routines")) { s = strrchr(s, ':') + 1; } if (w->is_server) { if (w->errbuf[0]) { strlcat(w->errbuf, ",S:", sizeof w->errbuf); } else { strlcat(w->errbuf, "S:", sizeof w->errbuf); } strlcat(w->errbuf, s, sizeof w->errbuf); } else { if (w->errbuf[0]) { strlcat(w->errbuf, ",C:", sizeof w->errbuf); } else { strlcat(w->errbuf, "C:", sizeof w->errbuf); } strlcat(w->errbuf, s, sizeof w->errbuf); } w->wstate = CLOSED; } static const char *check_errors(struct Worker *client, struct Worker *server) { static char buf[1024]; if (!client->errbuf[0] && !server->errbuf[0]) { if (server->show) { strlcpy(buf, server->showbuf, sizeof buf); return buf; } if (client->show) { strlcpy(buf, client->showbuf, sizeof buf); return buf; } return "OK"; } buf[0] = 0; strlcat(buf, client->errbuf, sizeof buf); if (buf[0] && server->errbuf[0]) strlcat(buf, " - ", sizeof buf); strlcat(buf, server->errbuf, sizeof buf); return buf; } static const char *create_worker(struct Worker **w_p, bool is_server, ...) { va_list ap; const char *k, *v; int klen; struct Worker *w; int err; const char *mem = NULL; void *fdata; size_t flen; const char *errmsg = NULL; *w_p = NULL; w = calloc(1, sizeof *w); if (!w) return "calloc"; w->wstate = HANDSHAKE; w->is_server = is_server; w->config = tls_config_new(); if (!w->config) return "tls_config_new failed"; if (is_server) { w->base = tls_server(); if (!w->base) return "tls_server failed"; } else { w->ctx = tls_client(); if (!w->ctx) return "tls_client failed"; } va_start(ap, is_server); while (1) { k = va_arg(ap, char *); if (!k) break; v = strchr(k, '='); if (!v) { errmsg = k; break; } v++; klen = v - k; err = 0; if (!strncmp(k, "mem=", klen)) { mem = v; } else if (!strncmp(k, "ca=", klen)) { if (mem) { fdata = load_file(v, &flen); if (!fdata) { errmsg = strerror(errno); break; } err = tls_config_set_ca_mem(w->config, fdata, flen); free(fdata); } else { err = tls_config_set_ca_file(w->config, v); } } else if (!strncmp(k, "cert=", klen)) { if (mem) { fdata = load_file(v, &flen); if (!fdata) { errmsg = strerror(errno); break; } err = tls_config_set_cert_mem(w->config, fdata, flen); free(fdata); } else { err = tls_config_set_cert_file(w->config, v); } } else if (!strncmp(k, "key=", klen)) { if (mem) { fdata = load_file(v, &flen); if (!fdata) { errmsg = strerror(errno); break; } err = tls_config_set_key_mem(w->config, fdata, flen); free(fdata); } else { err = tls_config_set_key_file(w->config, v); } } else if (!strncmp(k, "show=", klen)) { w->show = v; } else if (!strncmp(k, "ciphers=", klen)) { err = tls_config_set_ciphers(w->config, v); } else if (!strncmp(k, "host=", klen)) { w->hostname = v; } else if (!strncmp(k, "noverifycert=", klen)) { tls_config_insecure_noverifycert(w->config); } else if (!strncmp(k, "noverifyname=", klen)) { tls_config_insecure_noverifyname(w->config); } else if (!strncmp(k, "verify=", klen)) { tls_config_verify(w->config); } else if (!strncmp(k, "dheparams=", klen)) { err = tls_config_set_dheparams(w->config, v); } else if (!strncmp(k, "ecdhecurve=", klen)) { err = tls_config_set_ecdhecurve(w->config, v); } else if (!strncmp(k, "protocols=", klen)) { uint32_t protos; err = tls_config_parse_protocols(&protos, v); tls_config_set_protocols(w->config, protos); } else if (!strncmp(k, "peer-sha1=", klen)) { w->peer_fingerprint_sha1 = v; } else if (!strncmp(k, "peer-sha256=", klen)) { w->peer_fingerprint_sha256 = v; } else if (!strncmp(k, "verify-client=", klen)) { tls_config_verify_client(w->config); } else if (!strncmp(k, "verify-client-optional=", klen)) { tls_config_verify_client_optional(w->config); } else if (!strncmp(k, "aggressive-close=", klen)) { w->aggressive_close = 1; } else { errmsg = k; break; } if (err < 0) { errmsg = k; break; } } va_end(ap); if (errmsg) return errmsg; if (is_server) { if (tls_configure(w->base, w->config) < 0) return tls_error(w->base); } else { if (tls_configure(w->ctx, w->config) < 0) return tls_error(w->ctx); } *w_p = w; return "OK"; } static const char *do_handshake(struct Worker *w, int fd); static const char *wait_for_event(struct Worker *w, short flag); static void worker_cb(int fd, short flags, void *arg) { struct Worker *w = arg; const char *err; char buf[128]; int res; w->pending = 0; if (w->wstate == HANDSHAKE) { err = do_handshake(w, fd); add_error(w, "%s", err); } else if (w->wstate == CONNECTED) { if (flags & EV_READ) { res = tls_read(w->ctx, buf, sizeof buf); if (res == TLS_WANT_POLLIN) { wait_for_event(w, EV_READ); } else if (res == TLS_WANT_POLLOUT) { wait_for_event(w, EV_WRITE); } else if (res >= 0) { if (res > 0 && w->is_server) { res = tls_write(w->ctx, "END", 3); w->wstate = CLOSED; } else if (res == 0) { w->wstate = CLOSED; } else { wait_for_event(w, EV_READ); } } else { add_error(w, "bad pkt: res=%d err=%s", res, tls_error(w->ctx)); } } else { add_error(w, "EV_WRITE?"); } } if (w->wstate == CLOSED && w->ctx) { if (w->aggressive_close) { close(w->socket); tls_close(w->ctx); res = 0; } else { res = tls_close(w->ctx); } if (res == 0) { tls_free(w->ctx); w->ctx = NULL; } else if (res == TLS_WANT_POLLIN) { wait_for_event(w, EV_READ); } else if (res == TLS_WANT_POLLOUT) { wait_for_event(w, EV_WRITE); } else { add_error(w, "close error: res=%d err=%s", res, tls_error(w->ctx)); tls_free(w->ctx); w->ctx = NULL; } } if (!w->pending && w->ctx) { errx(1, "missed event setup: %s flags=%d state=%d", w->is_server ? "S":"C", flags, w->wstate); } return; } static const char *mkhex(const uint8_t *src, int len, char *dst) { static const char hextbl[] = "0123456789abcdef"; int i; for (i = 0; i < len; i++) { dst[i*2] = hextbl[src[i] >> 4]; dst[i*2+1] = hextbl[src[i] & 15]; } dst[i*2] = 0; return dst; } static const char *hexcmp(const char *fn, const void *buf, unsigned int len) { char hexbuf[256]; size_t flen; char *fdata = load_file(fn, &flen); int cmp; if (!fdata) return strerror(errno); while (flen && isspace(fdata[flen-1])) flen--; fdata[flen] = 0; mkhex(buf, len, hexbuf); cmp = strcmp(hexbuf, fdata); free(fdata); return cmp ? "Fingerprint does not match" : NULL; } static const char *check_fp(struct Worker *w, const char *algo, const char *fn, size_t xlen) { const char *emsg; int res; struct tls_cert *cert = NULL; static char buf[1024]; if (!fn) return NULL; res = tls_get_peer_cert(w->ctx, &cert, algo); if (res != 0) { snprintf(buf, sizeof buf, "fp-cert: %s", tls_error(w->ctx)); return buf; } if (cert->fingerprint_size != xlen) { tls_cert_free(cert); return "FP-sha1-fail"; } emsg = hexcmp(fn, cert->fingerprint, cert->fingerprint_size); tls_cert_free(cert); if (emsg) return emsg; return NULL; } static void show_append(char *buf, size_t buflen, const char *s1, const char *s2) { if (!s1 || !s2) return; strlcat(buf, s1, buflen); strlcat(buf, s2, buflen); } static void show_dname(char *buf, size_t buflen, const struct tls_cert_dname *dname) { show_append(buf, buflen, "/CN=", dname->common_name); show_append(buf, buflen, "/C=", dname->country_name); show_append(buf, buflen, "/ST=", dname->state_or_province_name); show_append(buf, buflen, "/L=", dname->locality_name); show_append(buf, buflen, "/A=", dname->street_address); show_append(buf, buflen, "/O=", dname->organization_name); show_append(buf, buflen, "/OU=", dname->organizational_unit_name); } static const char *isotime(char *dst, size_t max, time_t t) { static char buf[32]; struct tm tm; if (!dst) { dst = buf; max = sizeof buf; } memset(&tm, 0, sizeof tm); gmtime_r(&t, &tm); strftime(dst, max, "%Y-%m-%dT%H:%M:%SZ", &tm); return dst; } static void show_cert(struct tls_cert *cert, char *buf, size_t buflen) { if (!cert) { snprintf(buf, buflen, "no cert"); return; } show_append(buf, buflen, "Subject: ", ""); show_dname(buf, buflen, &cert->subject); show_append(buf, buflen, " Issuer: ", ""); show_dname(buf, buflen, &cert->issuer); show_append(buf, buflen, " Serial: ", cert->serial); show_append(buf, buflen, " NotBefore: ", isotime(NULL, 0, cert->not_before)); show_append(buf, buflen, " NotAfter: ", isotime(NULL, 0, cert->not_after)); } static const char *done_handshake(struct Worker *w) { int res; const char *emsg; emsg = check_fp(w, "sha1", w->peer_fingerprint_sha1, 20); if (emsg) return emsg; emsg = check_fp(w, "sha256", w->peer_fingerprint_sha256, 32); if (emsg) return emsg; if (w->show) { if (strcmp(w->show, "ciphers") == 0) { tls_get_connection_info(w->ctx, w->showbuf, sizeof w->showbuf); } else if (strcmp(w->show, "peer-cert") == 0) { struct tls_cert *cert = NULL; tls_get_peer_cert(w->ctx, &cert, NULL); show_cert(cert, w->showbuf, sizeof w->showbuf); tls_cert_free(cert); } else { snprintf(w->showbuf, sizeof w->showbuf, "bad kw: show=%s", w->show); } } if (w->aggressive_close) { close(w->socket); tls_close(w->ctx); w->wstate = CLOSED; return "OK"; } if (!w->is_server) { res = tls_write(w->ctx, "PKT", 3); if (res < 0) { return tls_error(w->ctx); } else if (res == 0) { return "write==0"; } else if (res != 3) { return "write!=3"; } } return wait_for_event(w, EV_READ); } static const char *wait_for_event(struct Worker *w, short flags) { event_assign(&w->ev, w->evbase, w->socket, flags, worker_cb, w); tt_assert(event_add(&w->ev, NULL) == 0); w->pending = 1; return "OK"; end: return "event_add failed"; } static const char *do_handshake(struct Worker *w, int fd) { int err; const char *msg; err = tls_handshake(w->ctx); if (err == TLS_WANT_POLLIN) { return wait_for_event(w, EV_READ); } else if (err == TLS_WANT_POLLIN) { return wait_for_event(w, EV_WRITE); } else if (err == 0) { w->wstate = CONNECTED; return done_handshake(w); } msg = tls_error(w->ctx ? w->ctx : w->base); return msg ? msg : "handshake failure"; } static const char *start_worker(struct Worker *w, int fd) { int err; w->socket = fd; if (w->is_server) { err = tls_accept_socket(w->base, &w->ctx, fd); } else { err = tls_connect_socket(w->ctx, fd, w->hostname); } if (err != 0) { return tls_error(w->ctx ? w->ctx : w->base); } return do_handshake(w, fd); } static void ignore_sigpipe(void) { #ifndef WIN32 static bool done; sigset_t set; int ret; if (done) return; /* block SIGPIPE */ sigemptyset(&set); sigaddset(&set, SIGPIPE); ret = sigprocmask(SIG_BLOCK, &set, NULL); if (ret < 0) err(1, "sigprocmask"); done = true; #endif } static const char *run_case(struct Worker *client, struct Worker *server) { struct event_base *base = NULL; int spair[2]; const char *res = "huh"; bool done = false; ignore_sigpipe(); base = event_init(); client->evbase = base; server->evbase = base; tt_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, spair) == 0); tt_assert(socket_setup(spair[0], true)); tt_assert(socket_setup(spair[1], true)); str_check(start_worker(client, spair[1]), "OK"); str_check(start_worker(server, spair[0]), "OK"); while (client->ctx || server->ctx) tt_assert(event_base_loop(base, EVLOOP_ONCE) == 0); done = true; end: res = check_errors(client, server); free_worker(client); free_worker(server); event_base_free(base); return done ? res : "fail"; } /* * Actual testcases. */ #define SERVER1 "key=ssl/ca1_server1.key", "cert=ssl/ca1_server1.crt" #define SERVER2 "key=ssl/ca2_server2.key", "cert=ssl/ca2_server2.crt" #define CLIENT1 "key=ssl/ca1_client1.key", "cert=ssl/ca1_client1.crt" #define CLIENT2 "key=ssl/ca2_client2.key", "cert=ssl/ca2_client2.crt" #define CA1 "ca=ssl/ca1_root.crt" #define CA2 "ca=ssl/ca2_root.crt" #define COMPLEX1 "key=ssl/ca1_complex1.key", "cert=ssl/ca1_complex1.crt" #define COMPLEX2 "key=ssl/ca2_complex2.key", "cert=ssl/ca2_complex2.crt" static void test_verify(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* default: client checks server cert, succeeds */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA1, "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "OK"); /* default: client checks server cert, fails due to bad ca */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA2, "host=example.com", NULL), "OK"); str_any2(run_case(client, server), "C:certificate verify failed - S:tlsv1 alert unknown ca", "C:certificate verify failed - S:tlsv1 alert unknown ca,S:shutdown while in init"); /* default: client checks server cert, fails due to bad hostname */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA1, "host=example2.com", NULL), "OK"); str_check(run_case(client, server), "C:name `example2.com' not present in server certificate"); #if 0 /* client: aggressive close */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA1, "aggressive-close=1", "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "S:bad pkt: res=-1 err=read failed: EOF,S:close error: res=-1 err=shutdown failed: Broken pipe"); /* server: aggressive close */ str_check(create_worker(&server, true, SERVER1, "aggressive-close=1", NULL), "OK"); str_check(create_worker(&client, false, CA1, "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "C:write failed: Broken pipe,C:close error: res=-1 err=shutdown failed: Success"); #endif end:; } static void test_noverifyname(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* noverifyname: client checks server cert, ignore bad hostname */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA1, "host=example2.com", "noverifyname=1", NULL), "OK"); str_check(run_case(client, server), "OK"); /* noverifyname: client checks server cert, ignore NULL hostname */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA1, "noverifyname=1", NULL), "OK"); str_check(run_case(client, server), "OK"); end:; } static void test_noverifycert(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* noverifycert: client ignores cert */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA2, "host=server1.com", "noverifycert=1", NULL), "OK"); str_check(run_case(client, server), "OK"); /* noverifycert: client ignores cert, but checks hostname */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA2, "host=server2.com", "noverifycert=1", NULL), "OK"); str_check(run_case(client, server), "C:name `server2.com' not present in server certificate"); /* noverifycert: client ignores both cert, hostname */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA2, "host=server2.com", "noverifycert=1", "noverifyname=1", NULL), "OK"); str_check(run_case(client, server), "OK"); /* noverifycert: client ignores both cert, hostname (=NULL) */ str_check(create_worker(&server, true, SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA2, "noverifycert=1", "noverifyname=1", NULL), "OK"); str_check(run_case(client, server), "OK"); end:; } static void test_clientcert(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* ok: server checks client cert */ str_check(create_worker(&server, true, SERVER1, CA2, "verify-client=1", NULL), "OK"); str_check(create_worker(&client, false, CLIENT2, CA1, "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "OK"); /* fail: server rejects invalid cert */ str_check(create_worker(&server, true, SERVER1, CA1, "verify-client=1", NULL), "OK"); str_check(create_worker(&client, false, CLIENT2, CA1, "host=server1.com", NULL), "OK"); str_any3(run_case(client, server), "C:tlsv1 alert unknown ca - S:no certificate returned", "C:tlsv1 alert unknown ca,C:shutdown while in init - S:certificate verify failed", "C:tlsv1 alert unknown ca - S:certificate verify failed"); /* noverifycert: server allow invalid cert */ str_check(create_worker(&server, true, SERVER1, CA1, "noverifycert=1", NULL), "OK"); str_check(create_worker(&client, false, CLIENT2, CA1, "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "OK"); /* verify-client: don't allow client without cert */ str_check(create_worker(&server, true, SERVER1, CA2, "verify-client=1", NULL), "OK"); str_check(create_worker(&client, false, CA1, "host=server1.com", NULL), "OK"); str_any2(run_case(client, server), "C:sslv3 alert handshake failure - S:peer did not return a certificate", "C:sslv3 alert handshake failure,C:shutdown while in init - S:peer did not return a certificate"); /* verify-client-optional: allow client without cert */ str_check(create_worker(&server, true, SERVER1, CA2, "verify-client-optional=1", NULL), "OK"); str_check(create_worker(&client, false, CA1, "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "OK"); end:; } static void test_fingerprint(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* both server & client with cert */ str_check(create_worker(&server, true, SERVER1, CA2, "verify-client=1", "peer-sha1=ssl/ca2_client2.crt.sha1", "peer-sha256=ssl/ca2_client2.crt.sha256", NULL), "OK"); str_check(create_worker(&client, false, CLIENT2, CA1, "host=server1.com", "peer-sha1=ssl/ca1_server1.crt.sha1", "peer-sha256=ssl/ca1_server1.crt.sha256", NULL), "OK"); str_check(run_case(client, server), "OK"); /* client without cert */ str_check(create_worker(&server, true, SERVER1, CA1, "verify-client=1", "peer-sha1=ssl/ca2_client2.crt.sha1", "peer-sha256=ssl/ca2_client2.crt.sha256", NULL), "OK"); str_check(create_worker(&client, false, CA1, "host=server1.com", NULL), "OK"); str_any2(run_case(client, server), "C:sslv3 alert handshake failure - S:peer did not return a certificate", "C:sslv3 alert handshake failure,C:shutdown while in init - S:peer did not return a certificate"); end:; } static void test_set_mem(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* both server & client with cert */ str_check(create_worker(&server, true, "mem=1", SERVER1, CA2, NULL), "OK"); str_check(create_worker(&client, false, "mem=1", CLIENT2, CA1, "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "OK"); end:; } static void test_cipher_nego(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* server key is EC:secp384r1 - ECDHE-ECDSA */ str_check(create_worker(&server, true, "show=ciphers", SERVER1, NULL), "OK"); str_check(create_worker(&client, false, CA1, "ciphers=AESGCM", "host=server1.com", NULL), "OK"); str_check(run_case(client, server), "TLSv1.2/ECDHE-ECDSA-AES256-GCM-SHA384/ECDH=secp384r1"); /* server key is RSA - ECDHE-RSA */ str_check(create_worker(&server, true, "show=ciphers", SERVER2, NULL), "OK"); str_check(create_worker(&client, false, CA2, "ciphers=AESGCM", "host=server2.com", NULL), "OK"); str_check(run_case(client, server), "TLSv1.2/ECDHE-RSA-AES256-GCM-SHA384/ECDH=prime256v1"); /* server key is RSA - DHE-RSA */ str_check(create_worker(&server, true, SERVER2, "show=ciphers", "dheparams=auto", NULL), "OK"); str_check(create_worker(&client, false, CA2, "ciphers=EDH+AESGCM", "host=server2.com", NULL), "OK"); str_check(run_case(client, server), "TLSv1.2/DHE-RSA-AES256-GCM-SHA384/DH=2048"); /* server key is RSA - ECDHE-RSA */ str_check(create_worker(&server, true, SERVER2, "show=ciphers", NULL), "OK"); str_check(create_worker(&client, false, CA2, "ciphers=EECDH+AES", "host=server2.com", NULL), "OK"); str_check(run_case(client, server), "TLSv1.2/ECDHE-RSA-AES256-GCM-SHA384/ECDH=prime256v1"); end:; } static void test_cert_info(void *z) { struct Worker *server = NULL, *client = NULL; tt_assert(tls_init() == 0); /* server shows client cert */ str_check(create_worker(&server, true, "show=peer-cert", SERVER1, CA2, "peer-sha1=ssl/ca2_client2.crt.sha1", "peer-sha256=ssl/ca2_client2.crt.sha256", "verify-client=1", NULL), "OK"); str_check(create_worker(&client, false, CLIENT2, CA1, "host=server1.com", "peer-sha1=ssl/ca1_server1.crt.sha1", "peer-sha256=ssl/ca1_server1.crt.sha256", NULL), "OK"); str_check(run_case(client, server), "Subject: /CN=client2/C=XX/ST=State2/L=City2/O=Org2" " Issuer: /CN=TestCA2" " Serial: 1387724136048036785122419970010419099185643835502" " NotBefore: 2010-01-01T08:05:00Z" " NotAfter: 2060-12-31T23:55:00Z"); /* client shows server cert - utf8 */ str_check(create_worker(&server, true, COMPLEX1, NULL), "OK"); str_check(create_worker(&client, false, CA1, "show=peer-cert", "host=complex1.com", NULL), "OK"); str_check(run_case(client, server), "Subject: /CN=complex1.com/ST=様々な論争を引き起こしてきた。/L=Kõzzä" " Issuer: /CN=TestCA1/C=AA/ST=State1/L=City1/O=Org1" " Serial: 1113692385315072860785465640275941003895485612482" " NotBefore: 2010-01-01T08:05:00Z" " NotAfter: 2060-12-31T23:55:00Z"); /* client shows server cert - t61/bmp */ str_check(create_worker(&server, true, COMPLEX2, NULL), "OK"); str_check(create_worker(&client, false, CA2, "show=peer-cert", "host=complex2.com", NULL), "OK"); str_check(run_case(client, server), "Subject: /CN=complex2.com/ST=様々な論争を引き起こしてきた。/L=Kõzzä" " Issuer: /CN=TestCA2" " Serial: 344032136906054686761742495217219742691739762030" " NotBefore: 2010-01-01T08:05:00Z" " NotAfter: 2060-12-31T23:55:00Z"); end:; } /* * Host name pattern matching. */ static const char *do_verify(const char *hostname, const char *commonName, ...) { #ifdef DISABLED_TEST struct tls ctx; struct tls_cert cert; struct tls_cert_general_name names[20], *alt; union { struct in_addr ip4; struct in6_addr ip6; } addrbuf[20]; int addrpos = 0, ret; va_list ap; const char *aname; memset(&ctx, 0, sizeof(ctx)); memset(&addrbuf, 0, sizeof(addrbuf)); memset(&cert, 0, sizeof(cert)); memset(&names, 0, sizeof(names)); cert.subject.common_name = commonName; cert.subject_alt_names = names; va_start(ap, commonName); while (1) { aname = va_arg(ap, char *); if (!aname) break; alt = &names[cert.subject_alt_name_count++]; if (!memcmp(aname, "dns:", 4)) { alt->name_type = TLS_CERT_GNAME_DNS; alt->name_value = aname + 4; } else if (!memcmp(aname, "ip4:", 4)) { alt->name_type = TLS_CERT_GNAME_IPv4; alt->name_value = &addrbuf[addrpos++]; if (inet_pton(AF_INET, aname + 4, (void*)alt->name_value) != 1) return aname; } else if (!memcmp(aname, "ip6:", 4)) { alt->name_type = TLS_CERT_GNAME_IPv6; alt->name_value = &addrbuf[addrpos++]; if (inet_pton(AF_INET6, aname + 4, (void*)alt->name_value) != 1) return aname; } else { return aname; } } va_end(ap); ret = tls_check_servername(&ctx, &cert, hostname); if (ctx.errmsg) free(ctx.errmsg); if (ret == 0) return "OK"; if (ret == -1) return "FAIL"; #endif return "Unexpected code"; } static void test_servername(void *_) { str_check(do_verify("localhost", NULL, NULL), "FAIL"); str_check(do_verify("www.foo.com", "foo.com", "dns:foo.com", "dns:www.foo.com", NULL), "OK"); str_check(do_verify("foo.com", "foo.com", "dns:foo.com", "dns:www.foo.com", NULL), "OK"); str_check(do_verify("faa.com", "foo.com", "dns:foo.com", "dns:www.foo.com", NULL), "FAIL"); str_check(do_verify("www.foo.com", "*.foo.com", NULL), "OK"); str_check(do_verify("foo.com", "*.foo.com", NULL), "FAIL"); str_check(do_verify("foo.com", ".foo.com", NULL), "FAIL"); str_check(do_verify("foo.co.uk", "*.co.uk", NULL), "OK"); str_check(do_verify("www.foo.com", "w*.foo.com", NULL), "FAIL"); str_check(do_verify("www.foo.com", "www.*.com", NULL), "FAIL"); str_check(do_verify("www.foo.com", "*", NULL), "FAIL"); str_check(do_verify("www.foo.com", "*.*.*", NULL), "FAIL"); str_check(do_verify("foo.com", "*.com", NULL), "FAIL"); str_check(do_verify("foo.com", "*.org", NULL), "FAIL"); str_check(do_verify("localhost", "*", NULL), "FAIL"); str_check(do_verify("foo.com", "foox.com", "ip4:11.11.11.11", "ip6:fefe::efef", "dns:foo.com", NULL), "OK"); str_check(do_verify("11.11.11.11", "foo.com", "ip4:11.11.11.11", "ip6:fefe::efef", NULL), "OK"); str_check(do_verify("11.11.11.12", "foo.com", "ip4:11.11.11.11", "ip6:fefe::efef", NULL), "FAIL"); str_check(do_verify("fefe::efef", "foo.com", "ip4:11.11.11.11", "ip6:fefe::efef", NULL), "OK"); str_check(do_verify("fefe::efff", "foo.com", "ip4:11.11.11.11", "ip6:fefe::efef", NULL), "FAIL"); str_check(do_verify("127.0.0.1", "foo.com", "ip4:127.0.0.1", NULL), "OK"); str_check(do_verify("127.0.0.1", "*.0.0.1", NULL), "FAIL"); str_check(do_verify("127.0.0.1", "127.0.0.*", NULL), "FAIL"); str_check(do_verify("fefe::efef", "*.efef", NULL), "FAIL"); str_check(do_verify("fefe::efef", "fefe::efef", NULL), "OK"); str_check(do_verify("fefe::efef", "foo", "dns:fefe::efef", NULL), "FAIL"); str_check(do_verify("1.1.1.1", "1.1.1.1", NULL), "OK"); str_check(do_verify("1.1.1.1", NULL, "dns:1.1.1.1", NULL), "FAIL"); end:; } /* * Test ASN.1 parse time. */ static const char *run_time(const char *val) { #ifdef USUAL_LIBSSL_FOR_TLS ASN1_TIME tmp; time_t t = 0; static char buf[128]; struct tm *tm, tmbuf; struct tls *ctx; int err; memset(&tmp, 0, sizeof tmp); memset(&tmbuf, 0, sizeof tmbuf); tmp.data = (unsigned char*)val+2; tmp.length = strlen(val+2); if (val[0] == 'G' && val[1] == ':') { tmp.type = V_ASN1_GENERALIZEDTIME; } else if (val[0] == 'U' && val[1] == ':') { tmp.type = V_ASN1_UTCTIME; } else { tmp.type = val[0]; } ctx = tls_client(); if (!ctx) return "NOMEM"; err = tls_asn1_parse_time(ctx, &tmp, &t); if (err) { strlcpy(buf, tls_error(ctx), sizeof buf); tls_free(ctx); return buf; } tls_free(ctx); tm = gmtime_r(&t, &tmbuf); if (!tm) return "E-GMTIME"; strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S GMT", tm); return buf; #else return "no-asn1"; #endif } static void test_time(void *_) { //str_check(run_time("U:1401010215Z"), "2014-01-01 02:15:00 GMT"); str_check(run_time("U:140101021500Z"), "2014-01-01 02:15:00 GMT"); //str_check(run_time("U:1401010215"), "2014-01-01 02:15:00 GMT"); //str_check(run_time("U:140101021500"), "2014-01-01 02:15:00 GMT"); str_check(run_time("U:14010102150"), "Invalid asn1 time"); str_check(run_time("U:"), "Invalid asn1 time"); str_check(run_time("X:"), "Invalid time object type: 88"); str_check(run_time("G:20140101021544Z"), "2014-01-01 02:15:44 GMT"); end:; } struct testcase_t tls_tests[] = { #ifndef USUAL_LIBSSL_FOR_TLS END_OF_TESTCASES, #endif { "time", test_time }, { "verify", test_verify }, { "noverifyname", test_noverifyname }, { "noverifycert", test_noverifycert }, { "clientcert", test_clientcert }, { "fingerprint", test_fingerprint }, { "set-mem", test_set_mem }, { "cipher-nego", test_cipher_nego }, { "cert-info", test_cert_info }, END_OF_TESTCASES, { "servername", test_servername }, }; pgqd/lib/test/test_cxalloc.c0000664000401600040160000000473613175113172014443 0ustar cbecbe#include "test_common.h" #include #include static int delta = 0; static char logbuf[1024]; static void reset(void) { logbuf[0] = 0; } _PRINTF(1,2) static void m_log(const char *fmt, ...) { size_t len = strlen(logbuf); va_list ap; if (len && len < sizeof(logbuf) - 1) logbuf[len++] = ' '; va_start(ap, fmt); vsnprintf(logbuf + len, sizeof(logbuf) - len, fmt, ap); va_end(ap); } static void *log_alloc(void *ctx, size_t len) { void *p; m_log("A(%d)", (int)len); delta += len; p = cx_alloc(ctx, len + 8); *(int*)p = len; return (char *)p + 8; } static void *log_realloc(void *ctx, void *ptr, size_t len) { char *p = (char *)ptr - 8; int olen = *(int*)p; m_log("R(%d)", (int)len); p = cx_realloc(ctx, p, len + 8); *(int*)p = len; delta += len - olen; return p + 8; } static void log_free(void *ctx, const void *ptr) { char *p = (char *)ptr - 8; int len = *(int*)p; delta -= len; m_log("F(%d)", len); cx_free(ctx, p); } static const struct CxOps log_ops = { log_alloc, log_realloc, log_free, }; static const struct CxMem log_libc = { &log_ops, (void*)&cx_libc_allocator, }; #define log_check(x) str_check(logbuf, x); reset(); static void test_cxalloc_basic(void *zzz) { CxMem *cx = &log_libc; void *p; delta = 0; p = cx_alloc(cx, 16); log_check("A(16)") p = cx_realloc(cx, p, 500); log_check("R(500)") cx_free(cx, p); log_check("F(500)"); int_check(delta, 0); end: reset(); } static void test_cxalloc_tree(void *zzz) { CxMem *cx1, *cx2; void *p; delta = 0; cx1 = cx_new_tree(&log_libc); p = cx_alloc(cx1, 16); p = cx_realloc(cx1, p, 500); p = cx_realloc(cx1, p, 1500); p = cx_alloc(cx1, 55); cx_free(cx1, p); cx2 = cx_new_tree(cx1); p = cx_realloc(cx2, NULL, 2500); cx2 = cx_new_tree(cx2); p = cx_realloc(cx2, NULL, 3500); cx_destroy(cx1); /* str_check(logbuf, "A(16)R(500)F()"); */ int_check(delta, 0); end: reset(); } static void test_cxalloc_util(void *zzz) { CxMem *cx = &log_libc; void *p; delta = 0; p = cx_strdup(cx, "3333"); log_check("A(5)"); str_check(p, "3333"); cx_free(cx, p); log_check("F(5)"); p = cx_memdup(cx, "9876543", 8); log_check("A(8)"); str_check(p, "9876543"); cx_free(cx, p); log_check("F(8)"); p = cx_sprintf(cx, "a=%s", "123"); log_check("A(6)") cx_free(cx, p); log_check("F(6)"); int_check(delta, 0); end:; } struct testcase_t cxalloc_tests[] = { { "basic", test_cxalloc_basic }, { "tree", test_cxalloc_tree }, { "util", test_cxalloc_util }, END_OF_TESTCASES }; pgqd/lib/test/force_compat.sed0000664000401600040160000000104113175113172014733 0ustar cbecbe/^#define.*FFS/s,.*,/* & */, /^#define.*FLS/s,.*,/* & */, /^#define.*STRLCPY/s,.*,/* & */, /^#define.*STRLCAT/s,.*,/* & */, /^#define.*BASENAME/s,.*,/* & */, /^#define.*DIRNAME/s,.*,/* & */, /^#define.*REGCOMP/s,.*,/* & */, /^#define.*GETADDRINFO_A/s,.*,/* & */, /^#define.*INET_NTOP/s,.*,/* & */, /^#define.*INET_PTON/s,.*,/* & */, /^#define.*GETOPT/s,.*,/* & */, /^#define.*CTYPE_ON_CHAR/s,.*,/* & */, /^#define.*FNMATCH/s,.*,/* & */, /^#define.*MBSNRTOWCS/s,.*,/* & */, /^#define.*GETENTROPY/s,.*,/* & */, /^#define.*ARC4RANDOM/s,.*,/* & */, pgqd/lib/test/test_cfparser.c0000664000401600040160000000641613175113172014620 0ustar cbecbe#include #include #include #include #include #include "test_common.h" struct Config1 { const char *str1; const char *def1; int int1; int bool1; }; struct Config2 { const char *str2; const char *def2; double time_double; usec_t time_usec; }; static struct Config1 cf1; static struct Config2 cf2; static void cleanup(void) { free(cf1.str1); free(cf1.def1); free(cf2.str2); free(cf2.def2); memset(&cf1, 0, sizeof(cf1)); memset(&cf2, 0, sizeof(cf2)); } static const struct CfKey keys1 [] = { CF_ABS("str1", CF_STR, cf1.str1, 0, NULL), CF_ABS("def1", CF_STR, cf1.def1, 0, NULL), CF_ABS("int", CF_INT, cf1.int1, 0, NULL), CF_ABS("bool", CF_BOOL, cf1.bool1, 0, NULL), { NULL }, }; static const struct CfKey keys2 [] = { CF_ABS("str2", CF_STR, cf2.str2, 0, NULL), CF_ABS("def2", CF_STR, cf2.def2, 0, "somedefault"), CF_ABS("time1", CF_TIME_USEC, cf2.time_usec, 0, NULL), CF_ABS("time2", CF_TIME_DOUBLE, cf2.time_double, 0, NULL), { NULL }, }; static const struct CfSect sects [] = { { "one", keys1 }, { "two", keys2 }, { NULL }, }; static struct CfContext cfdesc1 = { sects, NULL }; static void test_abs(void *ptr) { char buf[128]; int_check(1, cf_load_file(&cfdesc1, "test_cfparser.ini")); str_check(cf1.str1, "val1"); tt_assert(cf1.def1 == NULL); str_check(cf2.str2, "val2"); str_check(cf2.def2, "somedefault"); tt_assert(cf2.time_usec == (3 * USEC / 2)); tt_assert(cf2.time_double == 2.5); str_check("val1", cf_get(&cfdesc1, "one", "str1", buf, sizeof(buf))); int_check(1, cf_set(&cfdesc1, "one", "str1", "val2")); str_check("val2", cf_get(&cfdesc1, "one", "str1", buf, sizeof(buf))); end: cleanup(); } /* * relative addressing. */ #define CF_REL_BASE struct Config1 static const struct CfKey rkeys1 [] = { CF_REL("str1", CF_STR, str1, 0, NULL), CF_REL("def1", CF_STR, def1, 0, NULL), CF_REL("int", CF_INT, int1, 0, NULL), CF_REL("bool", CF_BOOL, bool1, 0, NULL), { NULL }, }; #undef CF_REL_BASE #define CF_REL_BASE struct Config2 static const struct CfKey rkeys2 [] = { CF_REL("str2", CF_STR, str2, 0, NULL), CF_REL("def2", CF_STR, def2, 0, "somedefault"), CF_REL("time1", CF_TIME_USEC, time_usec, 0, NULL), CF_REL("time2", CF_TIME_DOUBLE, time_double, 0, NULL), { NULL }, }; #undef CF_REL_BASE static void *get_two(void *top_arg, const char *sect_name) { return &cf2; } static const struct CfSect rsects [] = { { "one", rkeys1 }, { "two", rkeys2, get_two, }, { NULL }, }; static struct CfContext cfdesc2 = { rsects, &cf1 }; static void test_rel(void *ptr) { char buf[128]; const char *fn = "test_cfparser.ini"; cleanup(); if (file_size(fn) < 0) fn = "test/test_cfparser.ini"; int_check(1, cf_load_file(&cfdesc2, fn)); str_check(cf1.str1, "val1"); tt_assert(cf1.def1 == NULL); str_check(cf2.str2, "val2"); str_check(cf2.def2, "somedefault"); tt_assert(cf2.time_usec == (3 * USEC / 2)); tt_assert(cf2.time_double == 2.5); str_check("val1", cf_get(&cfdesc2, "one", "str1", buf, sizeof(buf))); int_check(1, cf_set(&cfdesc2, "one", "str1", "val2")); str_check("val2", cf_get(&cfdesc2, "one", "str1", buf, sizeof(buf))); end: cleanup(); } /* * Describe */ struct testcase_t cfparser_tests[] = { { "abs", test_abs }, { "rel", test_rel }, END_OF_TESTCASES }; pgqd/lib/test/test_heap.c0000664000401600040160000001012713175113172013722 0ustar cbecbe #include #include #include "test_common.h" struct MyNode { int value; unsigned heap_idx; }; /* min-heap */ static bool heap_is_better(const void *a, const void *b) { const struct MyNode *aa = a, *bb = b; return (aa->value < bb->value); } static void my_save_pos(void *p, unsigned i) { struct MyNode *node = p; node->heap_idx = i; } static char *OK = "OK"; static struct MyNode *make_node(int v) { struct MyNode *n = malloc(sizeof(*n)); n->value = v; n->heap_idx = -1; return n; } static unsigned _heap_get_child(unsigned i, unsigned child_nr) { return 2*i + 1 + child_nr; } static bool _heap_is_better(struct Heap *h, unsigned i1, unsigned i2) { return heap_is_better(heap_get_obj(h, i1), heap_get_obj(h, i2)); } /* * Test tree sanity */ static const char *mkerr(const char *msg, unsigned idx, int val) { static char buf[128]; snprintf(buf, sizeof(buf), "%s: idx=%d curval=%d", msg, idx, val); return buf; } static const char *check_sub(struct Heap *heap, unsigned idx, int i) { unsigned c0 = _heap_get_child(idx, 0); unsigned c1 = _heap_get_child(idx, 1); struct MyNode *n; const char *res; unsigned used = heap_size(heap); if (idx >= used) return OK; n = heap_get_obj(heap, idx); if (n->heap_idx != idx) return mkerr("wrong saved idx", idx, i); if (c0 < used && _heap_is_better(heap, c0, idx)) return mkerr("c0 wrong order", idx, i); if (c1 < used && _heap_is_better(heap, c1, idx)) return mkerr("c1 wrong order", idx, i); res = check_sub(heap, c0, i); if (res == OK) res = check_sub(heap, c1, i); return res; } static const char *check(struct Heap *heap, int i) { return check_sub(heap, 0, i); } /* * checking operations */ static const char *my_insert(struct Heap *heap, int value) { struct MyNode *my = make_node(value); if (!heap_push(heap, my)) return "FAIL"; return check(heap, value); } static const char *my_remove(struct Heap *h, unsigned idx) { struct MyNode *n; if (idx >= heap_size(h)) return "NEXIST"; n = heap_get_obj(h, idx); heap_remove(h, idx); free(n); return check(h, 0); } static const char *my_clean(struct Heap *heap) { const char *res; while (heap_size(heap) > 0) { res = my_remove(heap, 0); if (res != OK) return res; } return OK; } /* * Simple operations. */ static void test_heap_basic(void *p) { struct Heap *heap; int i; heap = heap_create(heap_is_better, my_save_pos, NULL); str_check(my_remove(heap, 0), "NEXIST"); str_check(my_insert(heap, 0), "OK"); str_check(my_remove(heap, 0), "OK"); for (i = 0; i < 15; i++) { str_check(my_insert(heap, i), "OK"); } str_check(my_clean(heap), "OK"); for (i = -1; i > -15; i--) { str_check(my_insert(heap, i), "OK"); } str_check(my_clean(heap), "OK"); for (i = 30; i < 45; i++) { str_check(my_insert(heap, i), "OK"); } str_check(my_clean(heap), "OK"); for (i = 15; i < 30; i++) { str_check(my_insert(heap, i), "OK"); } str_check(my_clean(heap), "OK"); end: heap_destroy(heap); } #if 0 /* * randomized test */ #define RSIZE 3000 static int get_next(bool with_stat, bool added[]) { int r = random() % RSIZE; int i = r; while (1) { if (added[i] == with_stat) return i; if (++i >= RSIZE) i = 0; if (i == r) return -1; } } static void test_aatree_random(void *p) { bool is_added[RSIZE]; int prefer_remove = 0; /* 0 - insert, 1 - delete */ int n; int op; /* 0 - insert, 1 - delete */ struct AATree tree[1]; unsigned long long total = 0; srandom(123123); memset(is_added, 0, sizeof(is_added)); aatree_init(tree, my_node_cmp, my_node_free); while (total < 100000) { int r = random() & 15; if (prefer_remove) op = r > 5; else op = r > 10; /* op = 0; */ n = get_next(op, is_added); if (n < 0) { if (prefer_remove == op) { prefer_remove = !prefer_remove; } continue; } if (op == 0) { str_check(my_insert(tree, n), "OK"); is_added[n] = 1; } else { str_check(my_remove(tree, n), "OK"); is_added[n] = 0; } total++; } end: aatree_destroy(tree); } #endif struct testcase_t heap_tests[] = { { "basic", test_heap_basic }, /* { "random", test_aatree_random }, */ END_OF_TESTCASES }; pgqd/lib/test/awk_test.sh0000775000401600040160000000135413175113172013764 0ustar cbecbe#! /bin/sh # test find_modules.sh vs. various awks # random awks that may be around awk_list="mawk gawk nawk oawk" awk_list="$awk_list heirloom-nawk heirloom-oawk" awk_list="$awk_list original-awk plan9-awk" fmod=../find_modules.sh dir=fmod_test usual_dir=.. rm -rf $dir mkdir $dir ok=1 for f in *.c; do printf "$f .. " # write reference with default 'awk' ref=$dir/$f.awk $fmod $usual_dir $f > $ref 2>&1 for a in $awk_list; do which $a > /dev/null || continue printf "$a " out=$dir/$f.$a AWK=$a \ $fmod $usual_dir $f > $out 2>&1 cmp -s $ref $out || { printf "(FAIL) " ok=0 } done echo "" done if test $ok = 1; then echo "All OK" else echo "FAIL: not all tests passed" exit 1 fi pgqd/lib/test/attregex/0000775000401600040160000000000013175113172013424 5ustar cbecbepgqd/lib/test/attregex/run.sh0000775000401600040160000000073213175113172014571 0ustar cbecbe#! /bin/sh rxtest=./testregex.libc rxtest=./testregex.usual tests="basic.dat categorize.dat nullsubexpr.dat" tests="$tests rightassoc.dat" #tests="$tests leftassoc.dat" tests="$tests forcedassoc.dat" tests="$tests repetition.dat" tests="$tests interpretation.dat" for t in $tests; do printf "%-20s" "$t" #$rxtest < data/$t | grep -vE '(NOTE|Research)' $rxtest < data/$t | tail -n +4 | grep -vE 'haskell|mimi|NOTE' done #$rxtest < data/categorize.dat | tail -n +4 pgqd/lib/test/attregex/data/0000775000401600040160000000000013175113172014335 5ustar cbecbepgqd/lib/test/attregex/data/leftassoc.dat0000664000401600040160000000114713175113172017015 0ustar cbecbeNOTE left-assoc:pass-all right-assoc:pass-none : 2002-04-29 E (a|ab)(c|bcd)(d*) abcd (0,4)(0,1)(1,4)(4,4) E (a|ab)(bcd|c)(d*) abcd (0,4)(0,1)(1,4)(4,4) E (ab|a)(c|bcd)(d*) abcd (0,4)(0,1)(1,4)(4,4) E (ab|a)(bcd|c)(d*) abcd (0,4)(0,1)(1,4)(4,4) E (a*)(b|abc)(c*) abc (0,3)(0,0)(0,3)(3,3) E (a*)(abc|b)(c*) abc (0,3)(0,0)(0,3)(3,3) E (a*)(b|abc)(c*) abc (0,3)(0,0)(0,3)(3,3) E (a*)(abc|b)(c*) abc (0,3)(0,0)(0,3)(3,3) E (a|ab)(c|bcd)(d|.*) abcd (0,4)(0,1)(1,4)(4,4) E (a|ab)(bcd|c)(d|.*) abcd (0,4)(0,1)(1,4)(4,4) E (ab|a)(c|bcd)(d|.*) abcd (0,4)(0,1)(1,4)(4,4) E (ab|a)(bcd|c)(d|.*) abcd (0,4)(0,1)(1,4)(4,4) pgqd/lib/test/attregex/data/nullsubexpr.dat0000664000401600040160000000353213175113172017415 0ustar cbecbeNOTE null subexpression matches : 2002-06-06 E (a*)* a (0,1)(0,1) E SAME x (0,0)(0,0) E SAME aaaaaa (0,6)(0,6) E SAME aaaaaax (0,6)(0,6) E (a*)+ a (0,1)(0,1) E SAME x (0,0)(0,0) E SAME aaaaaa (0,6)(0,6) E SAME aaaaaax (0,6)(0,6) E (a+)* a (0,1)(0,1) E SAME x (0,0) E SAME aaaaaa (0,6)(0,6) E SAME aaaaaax (0,6)(0,6) E (a+)+ a (0,1)(0,1) E SAME x NOMATCH E SAME aaaaaa (0,6)(0,6) E SAME aaaaaax (0,6)(0,6) E ([a]*)* a (0,1)(0,1) E SAME x (0,0)(0,0) E SAME aaaaaa (0,6)(0,6) E SAME aaaaaax (0,6)(0,6) E ([a]*)+ a (0,1)(0,1) E SAME x (0,0)(0,0) E SAME aaaaaa (0,6)(0,6) E SAME aaaaaax (0,6)(0,6) E ([^b]*)* a (0,1)(0,1) E SAME b (0,0)(0,0) E SAME aaaaaa (0,6)(0,6) E SAME aaaaaab (0,6)(0,6) E ([ab]*)* a (0,1)(0,1) E SAME aaaaaa (0,6)(0,6) E SAME ababab (0,6)(0,6) E SAME bababa (0,6)(0,6) E SAME b (0,1)(0,1) E SAME bbbbbb (0,6)(0,6) E SAME aaaabcde (0,5)(0,5) E ([^a]*)* b (0,1)(0,1) E SAME bbbbbb (0,6)(0,6) E SAME aaaaaa (0,0)(0,0) E ([^ab]*)* ccccxx (0,6)(0,6) E SAME ababab (0,0)(0,0) E ((z)+|a)* zabcde (0,2)(1,2) {E a+? aaaaaa (0,1) no *? +? mimimal match ops E (a) aaa (0,1)(0,1) E (a*?) aaa (0,0)(0,0) E (a)*? aaa (0,0) E (a*?)*? aaa (0,0) } B \(a*\)*\(x\) x (0,1)(0,0)(0,1) B \(a*\)*\(x\) ax (0,2)(0,1)(1,2) B \(a*\)*\(x\) axa (0,2)(0,1)(1,2) B \(a*\)*\(x\)\(\1\) x (0,1)(0,0)(0,1)(1,1) B \(a*\)*\(x\)\(\1\) ax (0,2)(1,1)(1,2)(2,2) B \(a*\)*\(x\)\(\1\) axa (0,3)(0,1)(1,2)(2,3) B \(a*\)*\(x\)\(\1\)\(x\) axax (0,4)(0,1)(1,2)(2,3)(3,4) B \(a*\)*\(x\)\(\1\)\(x\) axxa (0,3)(1,1)(1,2)(2,2)(2,3) E (a*)*(x) x (0,1)(0,0)(0,1) E (a*)*(x) ax (0,2)(0,1)(1,2) E (a*)*(x) axa (0,2)(0,1)(1,2) E (a*)+(x) x (0,1)(0,0)(0,1) E (a*)+(x) ax (0,2)(0,1)(1,2) E (a*)+(x) axa (0,2)(0,1)(1,2) E (a*){2}(x) x (0,1)(0,0)(0,1) E (a*){2}(x) ax (0,2)(1,1)(1,2) E (a*){2}(x) axa (0,2)(1,1)(1,2) pgqd/lib/test/attregex/data/basic.dat0000664000401600040160000002057313175113172016117 0ustar cbecbeNOTE all standard compliant implementations should pass these : 2002-05-31 BE abracadabra$ abracadabracadabra (7,18) BE a...b abababbb (2,7) BE XXXXXX ..XXXXXX (2,8) E \) () (1,2) BE a] a]a (0,2) B } } (0,1) E \} } (0,1) BE \] ] (0,1) B ] ] (0,1) E ] ] (0,1) B { { (0,1) B } } (0,1) BE ^a ax (0,1) BE \^a a^a (1,3) BE a\^ a^ (0,2) BE a$ aa (1,2) BE a\$ a$ (0,2) BE ^$ NULL (0,0) E $^ NULL (0,0) E a($) aa (1,2)(2,2) E a*(^a) aa (0,1)(0,1) E (..)*(...)* a (0,0) E (..)*(...)* abcd (0,4)(2,4) E (ab|a)(bc|c) abc (0,3)(0,2)(2,3) E (ab)c|abc abc (0,3)(0,2) E a{0}b ab (1,2) E (a*)(b?)(b+)b{3} aaabbbbbbb (0,10)(0,3)(3,4)(4,7) E (a*)(b{0,1})(b{1,})b{3} aaabbbbbbb (0,10)(0,3)(3,4)(4,7) E a{9876543210} NULL BADBR E ((a|a)|a) a (0,1)(0,1)(0,1) E (a*)(a|aa) aaaa (0,4)(0,3)(3,4) E a*(a.|aa) aaaa (0,4)(2,4) E a(b)|c(d)|a(e)f aef (0,3)(?,?)(?,?)(1,2) E (a|b)?.* b (0,1)(0,1) E (a|b)c|a(b|c) ac (0,2)(0,1) E (a|b)c|a(b|c) ab (0,2)(?,?)(1,2) E (a|b)*c|(a|ab)*c abc (0,3)(1,2) E (a|b)*c|(a|ab)*c xc (1,2) E (.a|.b).*|.*(.a|.b) xa (0,2)(0,2) E a?(ab|ba)ab abab (0,4)(0,2) E a?(ac{0}b|ba)ab abab (0,4)(0,2) E ab|abab abbabab (0,2) E aba|bab|bba baaabbbaba (5,8) E aba|bab baaabbbaba (6,9) E (aa|aaa)*|(a|aaaaa) aa (0,2)(0,2) E (a.|.a.)*|(a|.a...) aa (0,2)(0,2) E ab|a xabc (1,3) E ab|a xxabc (2,4) Ei (Ab|cD)* aBcD (0,4)(2,4) BE [^-] --a (2,3) BE [a-]* --a (0,3) BE [a-m-]* --amoma-- (0,4) E :::1:::0:|:::1:1:0: :::0:::1:::1:::0: (8,17) E :::1:::0:|:::1:1:1: :::0:::1:::1:::0: (8,17) {E [[:upper:]] A (0,1) [[]] not supported E [[:lower:]]+ `az{ (1,3) E [[:upper:]]+ @AZ[ (1,3) BE [[-]] [[-]] (2,4) BE [[.NIL.]] NULL ECOLLATE BE [[=aleph=]] NULL ECOLLATE } BE$ \n \n (0,1) BEn$ \n \n (0,1) BE$ [^a] \n (0,1) BE$ \na \na (0,2) E (a)(b)(c) abc (0,3)(0,1)(1,2)(2,3) BE xxx xxx (0,3) E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) feb 6, (0,6) E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) 2/7 (0,3) E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) feb 1,Feb 6 (5,11) E3 ((((((((((((((((((((((((((((((x)))))))))))))))))))))))))))))) x (0,1)(0,1)(0,1) E3 ((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))* xx (0,2)(1,2)(1,2) E a?(ab|ba)* ababababababababababababababababababababababababababababababababababababababababa (0,81)(79,81) E abaa|abbaa|abbbaa|abbbbaa ababbabbbabbbabbbbabbbbaa (18,25) E abaa|abbaa|abbbaa|abbbbaa ababbabbbabbbabbbbabaa (18,22) E aaac|aabc|abac|abbc|baac|babc|bbac|bbbc baaabbbabac (7,11) BE$ .* \x01\xff (0,2) E aaaa|bbbb|cccc|ddddd|eeeeee|fffffff|gggg|hhhh|iiiii|jjjjj|kkkkk|llll XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa (53,57) L aaaa\nbbbb\ncccc\nddddd\neeeeee\nfffffff\ngggg\nhhhh\niiiii\njjjjj\nkkkkk\nllll XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa NOMATCH E a*a*a*a*a*b aaaaaaaaab (0,10) BE ^ NULL (0,0) BE $ NULL (0,0) BE ^$ NULL (0,0) BE ^a$ a (0,1) BE abc abc (0,3) BE abc xabcy (1,4) BE abc ababc (2,5) BE ab*c abc (0,3) BE ab*bc abc (0,3) BE ab*bc abbc (0,4) BE ab*bc abbbbc (0,6) E ab+bc abbc (0,4) E ab+bc abbbbc (0,6) E ab?bc abbc (0,4) E ab?bc abc (0,3) E ab?c abc (0,3) BE ^abc$ abc (0,3) BE ^abc abcc (0,3) BE abc$ aabc (1,4) BE ^ abc (0,0) BE $ abc (3,3) BE a.c abc (0,3) BE a.c axc (0,3) BE a.*c axyzc (0,5) BE a[bc]d abd (0,3) BE a[b-d]e ace (0,3) BE a[b-d] aac (1,3) BE a[-b] a- (0,2) BE a[b-] a- (0,2) BE a] a] (0,2) BE a[]]b a]b (0,3) BE a[^bc]d aed (0,3) BE a[^-b]c adc (0,3) BE a[^]b]c adc (0,3) E ab|cd abc (0,2) E ab|cd abcd (0,2) E a\(b a(b (0,3) E a\(*b ab (0,2) E a\(*b a((b (0,4) E ((a)) abc (0,1)(0,1)(0,1) E (a)b(c) abc (0,3)(0,1)(2,3) E a+b+c aabbabc (4,7) E a* aaa (0,3) E (a*)* - (0,0)(0,0) E (a*)+ - (0,0)(0,0) E (a*|b)* - (0,0)(0,0) E (a+|b)* ab (0,2)(1,2) E (a+|b)+ ab (0,2)(1,2) E (a+|b)? ab (0,1)(0,1) BE [^ab]* cde (0,3) E (^)* - (0,0)(0,0) BE a* NULL (0,0) E ([abc])*d abbbcd (0,6)(4,5) E ([abc])*bcd abcd (0,4)(0,1) E a|b|c|d|e e (0,1) E (a|b|c|d|e)f ef (0,2)(0,1) E ((a*|b))* - (0,0)(0,0)(0,0) BE abcd*efg abcdefg (0,7) BE ab* xabyabbbz (1,3) BE ab* xayabbbz (1,2) E (ab|cd)e abcde (2,5)(2,4) BE [abhgefdc]ij hij (0,3) E (a|b)c*d abcd (1,4)(1,2) E (ab|ab*)bc abc (0,3)(0,1) E a([bc]*)c* abc (0,3)(1,3) E a([bc]*)(c*d) abcd (0,4)(1,3)(3,4) E a([bc]+)(c*d) abcd (0,4)(1,3)(3,4) E a([bc]*)(c+d) abcd (0,4)(1,2)(2,4) E a[bcd]*dcdcde adcdcde (0,7) E (ab|a)b*c abc (0,3)(0,2) E ((a)(b)c)(d) abcd (0,4)(0,3)(0,1)(1,2)(3,4) BE [A-Za-z_][A-Za-z0-9_]* alpha (0,5) E ^a(bc+|b[eh])g|.h$ abh (1,3) E (bc+d$|ef*g.|h?i(j|k)) effgz (0,5)(0,5) E (bc+d$|ef*g.|h?i(j|k)) ij (0,2)(0,2)(1,2) E (bc+d$|ef*g.|h?i(j|k)) reffgz (1,6)(1,6) E (((((((((a))))))))) a (0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1) BE multiple words multiple words yeah (0,14) E (.*)c(.*) abcde (0,5)(0,2)(3,5) BE abcd abcd (0,4) E a(bc)d abcd (0,4)(1,3) E a[-]?c ac (0,3) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Qaddafi (0,15)(?,?)(10,12) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mo'ammar Gadhafi (0,16)(?,?)(11,13) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Kaddafi (0,15)(?,?)(10,12) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Qadhafi (0,15)(?,?)(10,12) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Gadafi (0,14)(?,?)(10,11) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mu'ammar Qadafi (0,15)(?,?)(11,12) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moamar Gaddafi (0,14)(?,?)(9,11) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mu'ammar Qadhdhafi (0,18)(?,?)(13,15) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Khaddafi (0,16)(?,?)(11,13) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghaddafy (0,16)(?,?)(11,13) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghadafi (0,15)(?,?)(11,12) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghaddafi (0,16)(?,?)(11,13) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muamar Kaddafi (0,14)(?,?)(9,11) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Quathafi (0,16)(?,?)(11,13) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Gheddafi (0,16)(?,?)(11,13) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moammar Khadafy (0,15)(?,?)(11,12) E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moammar Qudhafi (0,15)(?,?)(10,12) E a+(b|c)*d+ aabcdd (0,6)(3,4) E ^.+$ vivi (0,4) E ^(.+)$ vivi (0,4)(0,4) E ^([^!.]+).att.com!(.+)$ gryphon.att.com!eby (0,19)(0,7)(16,19) E ^([^!]+!)?([^!]+)$ bas (0,3)(?,?)(0,3) E ^([^!]+!)?([^!]+)$ bar!bas (0,7)(0,4)(4,7) E ^([^!]+!)?([^!]+)$ foo!bas (0,7)(0,4)(4,7) E ^.+!([^!]+!)([^!]+)$ foo!bar!bas (0,11)(4,8)(8,11) E ((foo)|(bar))!bas bar!bas (0,7)(0,3)(?,?)(0,3) E ((foo)|(bar))!bas foo!bar!bas (4,11)(4,7)(?,?)(4,7) E ((foo)|(bar))!bas foo!bas (0,7)(0,3)(0,3) E ((foo)|bar)!bas bar!bas (0,7)(0,3) E ((foo)|bar)!bas foo!bar!bas (4,11)(4,7) E ((foo)|bar)!bas foo!bas (0,7)(0,3)(0,3) E (foo|(bar))!bas bar!bas (0,7)(0,3)(0,3) E (foo|(bar))!bas foo!bar!bas (4,11)(4,7)(4,7) E (foo|(bar))!bas foo!bas (0,7)(0,3) E (foo|bar)!bas bar!bas (0,7)(0,3) E (foo|bar)!bas foo!bar!bas (4,11)(4,7) E (foo|bar)!bas foo!bas (0,7)(0,3) E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas (0,11)(0,11)(?,?)(?,?)(4,8)(8,11) E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bas (0,3)(?,?)(0,3) E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bar!bas (0,7)(0,4)(4,7) E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bar!bas (0,11)(?,?)(?,?)(4,8)(8,11) E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bas (0,7)(0,4)(4,7) E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bas (0,3)(0,3)(?,?)(0,3) E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bar!bas (0,7)(0,7)(0,4)(4,7) E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas (0,11)(0,11)(?,?)(?,?)(4,8)(8,11) E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bas (0,7)(0,7)(0,4)(4,7) E .*(/XXX).* /XXX (0,4)(0,4) E .*(\\XXX).* \XXX (0,4)(0,4) E \\XXX \XXX (0,4) E .*(/000).* /000 (0,4)(0,4) E .*(\\000).* \000 (0,4)(0,4) E \\000 \000 (0,4) pgqd/lib/test/attregex/data/repetition.dat0000664000401600040160000001254013175113172017213 0ustar cbecbeNOTE implicit vs. explicit repetitions : 2009-02-02 # Glenn Fowler # conforming matches (column 4) must match one of the following BREs # NOMATCH # (0,.)\((\(.\),\(.\))(?,?)(\2,\3)\)* # (0,.)\((\(.\),\(.\))(\2,\3)(?,?)\)* # i.e., each 3-tuple has two identical elements and one (?,?) E ((..)|(.)) NULL NOMATCH E ((..)|(.))((..)|(.)) NULL NOMATCH E ((..)|(.))((..)|(.))((..)|(.)) NULL NOMATCH E ((..)|(.)){1} NULL NOMATCH E ((..)|(.)){2} NULL NOMATCH E ((..)|(.)){3} NULL NOMATCH E ((..)|(.))* NULL (0,0) E ((..)|(.)) a (0,1)(0,1)(?,?)(0,1) E ((..)|(.))((..)|(.)) a NOMATCH E ((..)|(.))((..)|(.))((..)|(.)) a NOMATCH E ((..)|(.)){1} a (0,1)(0,1)(?,?)(0,1) E ((..)|(.)){2} a NOMATCH E ((..)|(.)){3} a NOMATCH E ((..)|(.))* a (0,1)(0,1)(?,?)(0,1) E ((..)|(.)) aa (0,2)(0,2)(0,2)(?,?) E ((..)|(.))((..)|(.)) aa (0,2)(0,1)(?,?)(0,1)(1,2)(?,?)(1,2) E ((..)|(.))((..)|(.))((..)|(.)) aa NOMATCH E ((..)|(.)){1} aa (0,2)(0,2)(0,2)(?,?) E ((..)|(.)){2} aa (0,2)(1,2)(?,?)(1,2) E ((..)|(.)){3} aa NOMATCH E ((..)|(.))* aa (0,2)(0,2)(0,2)(?,?) E ((..)|(.)) aaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.))((..)|(.)) aaa (0,3)(0,2)(0,2)(?,?)(2,3)(?,?)(2,3) E ((..)|(.))((..)|(.))((..)|(.)) aaa (0,3)(0,1)(?,?)(0,1)(1,2)(?,?)(1,2)(2,3)(?,?)(2,3) E ((..)|(.)){1} aaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.)){2} aaa (0,3)(2,3)(?,?)(2,3) E ((..)|(.)){3} aaa (0,3)(2,3)(?,?)(2,3) E ((..)|(.))* aaa (0,3)(2,3)(?,?)(2,3) E ((..)|(.)) aaaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.))((..)|(.)) aaaa (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?) E ((..)|(.))((..)|(.))((..)|(.)) aaaa (0,4)(0,2)(0,2)(?,?)(2,3)(?,?)(2,3)(3,4)(?,?)(3,4) E ((..)|(.)){1} aaaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.)){2} aaaa (0,4)(2,4)(2,4)(?,?) E ((..)|(.)){3} aaaa (0,4)(3,4)(?,?)(3,4) E ((..)|(.))* aaaa (0,4)(2,4)(2,4)(?,?) E ((..)|(.)) aaaaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.))((..)|(.)) aaaaa (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?) E ((..)|(.))((..)|(.))((..)|(.)) aaaaa (0,5)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)(4,5)(?,?)(4,5) E ((..)|(.)){1} aaaaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.)){2} aaaaa (0,4)(2,4)(2,4)(?,?) E ((..)|(.)){3} aaaaa (0,5)(4,5)(?,?)(4,5) E ((..)|(.))* aaaaa (0,5)(4,5)(?,?)(4,5) E ((..)|(.)) aaaaaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.))((..)|(.)) aaaaaa (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?) E ((..)|(.))((..)|(.))((..)|(.)) aaaaaa (0,6)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)(4,6)(4,6)(?,?) E ((..)|(.)){1} aaaaaa (0,2)(0,2)(0,2)(?,?) E ((..)|(.)){2} aaaaaa (0,4)(2,4)(2,4)(?,?) E ((..)|(.)){3} aaaaaa (0,6)(4,6)(4,6)(?,?) E ((..)|(.))* aaaaaa (0,6)(4,6)(4,6)(?,?) NOTE additional repetition tests graciously provided by Chris Kuklewicz www.haskell.org 2009-02-02 # These test a bug in OS X / FreeBSD / NetBSD, and libtree. # Linux/GLIBC gets the {8,} and {8,8} wrong. :HA#100:E X(.?){0,}Y X1234567Y (0,9)(7,8) :HA#101:E X(.?){1,}Y X1234567Y (0,9)(7,8) :HA#102:E X(.?){2,}Y X1234567Y (0,9)(7,8) :HA#103:E X(.?){3,}Y X1234567Y (0,9)(7,8) :HA#104:E X(.?){4,}Y X1234567Y (0,9)(7,8) :HA#105:E X(.?){5,}Y X1234567Y (0,9)(7,8) :HA#106:E X(.?){6,}Y X1234567Y (0,9)(7,8) :HA#107:E X(.?){7,}Y X1234567Y (0,9)(7,8) :HA#108:E X(.?){8,}Y X1234567Y (0,9)(8,8) :HA#110:E X(.?){0,8}Y X1234567Y (0,9)(7,8) :HA#111:E X(.?){1,8}Y X1234567Y (0,9)(7,8) :HA#112:E X(.?){2,8}Y X1234567Y (0,9)(7,8) :HA#113:E X(.?){3,8}Y X1234567Y (0,9)(7,8) :HA#114:E X(.?){4,8}Y X1234567Y (0,9)(7,8) :HA#115:E X(.?){5,8}Y X1234567Y (0,9)(7,8) :HA#116:E X(.?){6,8}Y X1234567Y (0,9)(7,8) :HA#117:E X(.?){7,8}Y X1234567Y (0,9)(7,8) :HA#118:E X(.?){8,8}Y X1234567Y (0,9)(8,8) # These test a fixed bug in my regex-tdfa that did not keep the expanded # form properly grouped, so right association did the wrong thing with # these ambiguous patterns (crafted just to test my code when I became # suspicious of my implementation). The first subexpression should use # "ab" then "a" then "bcd". # OS X / FreeBSD / NetBSD badly fail many of these, with impossible # results like (0,6)(4,5)(6,6). :HA#260:E (a|ab|c|bcd){0,}(d*) ababcd (0,6)(3,6)(6,6) :HA#261:E (a|ab|c|bcd){1,}(d*) ababcd (0,6)(3,6)(6,6) :HA#262:E (a|ab|c|bcd){2,}(d*) ababcd (0,6)(3,6)(6,6) :HA#263:E (a|ab|c|bcd){3,}(d*) ababcd (0,6)(3,6)(6,6) :HA#264:E (a|ab|c|bcd){4,}(d*) ababcd NOMATCH :HA#265:E (a|ab|c|bcd){0,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#266:E (a|ab|c|bcd){1,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#267:E (a|ab|c|bcd){2,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#268:E (a|ab|c|bcd){3,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#269:E (a|ab|c|bcd){4,10}(d*) ababcd NOMATCH :HA#270:E (a|ab|c|bcd)*(d*) ababcd (0,6)(3,6)(6,6) :HA#271:E (a|ab|c|bcd)+(d*) ababcd (0,6)(3,6)(6,6) # The above worked on Linux/GLIBC but the following often fail. # They also trip up OS X / FreeBSD / NetBSD: :HA#280:E (ab|a|c|bcd){0,}(d*) ababcd (0,6)(3,6)(6,6) :HA#281:E (ab|a|c|bcd){1,}(d*) ababcd (0,6)(3,6)(6,6) :HA#282:E (ab|a|c|bcd){2,}(d*) ababcd (0,6)(3,6)(6,6) :HA#283:E (ab|a|c|bcd){3,}(d*) ababcd (0,6)(3,6)(6,6) :HA#284:E (ab|a|c|bcd){4,}(d*) ababcd NOMATCH :HA#285:E (ab|a|c|bcd){0,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#286:E (ab|a|c|bcd){1,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#287:E (ab|a|c|bcd){2,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#288:E (ab|a|c|bcd){3,10}(d*) ababcd (0,6)(3,6)(6,6) :HA#289:E (ab|a|c|bcd){4,10}(d*) ababcd NOMATCH :HA#290:E (ab|a|c|bcd)*(d*) ababcd (0,6)(3,6)(6,6) :HA#291:E (ab|a|c|bcd)+(d*) ababcd (0,6)(3,6)(6,6) pgqd/lib/test/attregex/data/rightassoc.dat0000664000401600040160000000114713175113172017200 0ustar cbecbeNOTE left-assoc:pass-none right-assoc:pass-all : 2002-04-29 E (a|ab)(c|bcd)(d*) abcd (0,4)(0,2)(2,3)(3,4) E (a|ab)(bcd|c)(d*) abcd (0,4)(0,2)(2,3)(3,4) E (ab|a)(c|bcd)(d*) abcd (0,4)(0,2)(2,3)(3,4) E (ab|a)(bcd|c)(d*) abcd (0,4)(0,2)(2,3)(3,4) E (a*)(b|abc)(c*) abc (0,3)(0,1)(1,2)(2,3) E (a*)(abc|b)(c*) abc (0,3)(0,1)(1,2)(2,3) E (a*)(b|abc)(c*) abc (0,3)(0,1)(1,2)(2,3) E (a*)(abc|b)(c*) abc (0,3)(0,1)(1,2)(2,3) E (a|ab)(c|bcd)(d|.*) abcd (0,4)(0,2)(2,3)(3,4) E (a|ab)(bcd|c)(d|.*) abcd (0,4)(0,2)(2,3)(3,4) E (ab|a)(c|bcd)(d|.*) abcd (0,4)(0,2)(2,3)(3,4) E (ab|a)(bcd|c)(d|.*) abcd (0,4)(0,2)(2,3)(3,4) pgqd/lib/test/attregex/data/categorize.dat0000664000401600040160000000443013175113172017164 0ustar cbecbeNOTE regex implementation categorization 2004-05-31 ?E aa* xaxaax (1,2) POSITION=leftmost ; POSITION=bug ?E (a*)(ab)*(b*) abc (0,2)(0,1)(?,?)(1,2) ASSOCIATIVITY=right |E (a*)(ab)*(b*) abc (0,2)(0,0)(0,2)(2,2) ASSOCIATIVITY=left ; ASSOCIATIVITY=bug ?E ((a*)(ab)*)((b*)(a*)) aba (0,3)(0,2)(0,0)(0,2)(2,3)(2,2)(2,3) SUBEXPRESSION=precedence |E ((a*)(ab)*)((b*)(a*)) aba (0,3)(0,1)(0,1)(?,?)(1,3)(1,2)(2,3) SUBEXPRESSION=grouping ; SUBEXPRESSION=bug ?E (...?.?)* xxxxxx (0,6)(4,6) REPEAT_LONGEST=first |E (...?.?)* xxxxxx (0,6)(2,6) REPEAT_LONGEST=last |E (...?.?)* xxxxxx OK REPEAT_LONGEST=unknown ; REPEAT_LONGEST=bug ?E (a|ab)(bc|c) abcabc (0,3)(0,2)(2,3) EXPECTED |E (a|ab)(bc|c) abcabc (0,3)(0,1)(1,3) BUG=alternation-order ; BUG=alternation-order-UNKNOWN ?E (aba|a*b)(aba|a*b) ababa (0,5)(0,2)(2,5) EXPECTED |E (aba|a*b)(aba|a*b) ababa (0,4)(0,3)(3,4) BUG=first-match ; BUG=unknown-match ?B a\(b\)*\1 a NOMATCH EXPECTED |B a\(b\)*\1 a (0,1) BUG=nomatch-match |B a\(b\)*\1 abab (0,2)(1,2) # BUG=repeat-any ; BUG=nomatch-match-UNKNOWN ?E (a*){2} xxxxx (0,0)(0,0) EXPECTED |E (a*){2} xxxxx (5,5)(5,5) BUG=range-null ; BUG=range-null-UNKNOWN ?B a\(b\)*\1 abab NOMATCH EXPECTED |B a\(b\)*\1 abab (0,1) # BUG=nomatch-match |B a\(b\)*\1 abab (0,2)(1,2) BUG=repeat-any ; BUG=repeat-any-UNKNOWN ?E (a*)* a (0,1)(0,1) EXPECTED |E (a*)* ax (0,1)(0,1) BUG=repeat-null-unknown |E (a*)* a (0,1)(1,1) BUG=repeat-null ; BUG=repeat-null-UNKNOWN ?E (aba|a*b)* ababa (0,5)(2,5) EXPECTED |E (aba|a*b)* ababa (0,5)(3,4) BUG=repeat-short |E (aba|a*b)* ababa (0,4)(3,4) # LENGTH=first ; BUG=repeat-short-UNKNOWN ?E (a(b)?)+ aba (0,3)(2,3) EXPECTED |E (a(b)?)+ aba (0,3)(2,3)(1,2) BUG=repeat-artifact ; BUG=repeat-artifact-UNKNOWN ?B \(a\(b\)*\)*\2 abab NOMATCH EXPECTED |B \(a\(b\)*\)*\2 abab (0,4)(2,3)(1,2) BUG=repeat-artifact-nomatch ; BUG=repeat-artifact-nomatch-UNKNOWN ?E (a?)((ab)?)(b?)a?(ab)?b? abab (0,4)(0,1)(1,1)(?,?)(1,2)(2,4) BUG=subexpression-first |E .*(.*) ab (0,2)(2,2) EXPECTED |E .*(.*) ab (0,2)(0,2) BUG=subexpression-first ; BUG=subexpression-first-UNKNOWN pgqd/lib/test/attregex/data/interpretation.dat0000664000401600040160000000760713175113172020110 0ustar cbecbe:RE#01:E a+ xaax (1,3) :RE#02:B .\(a*\). xaax (0,4)(1,3) :RE#03:E (a?)((ab)?) ab (0,2)(0,0)(0,2)(0,2) :RE#04:E (a?)((ab)?)(b?) ab (0,2)(0,1)(1,1)(?,?)(1,2) :RE#05:E ((a?)((ab)?))(b?) ab (0,2)(0,2)(0,0)(0,2)(0,2)(2,2) :RE#06:E (a?)(((ab)?)(b?)) ab (0,2)(0,1)(1,2)(1,1)(?,?)(1,2) :RE#07:E (.?) x (0,1)(0,1) :RE#08:E (.?){1} x (0,1)(0,1) :RE#09:E (.?)(.?) x (0,1)(0,1)(1,1) :RE#10:E (.?){2} x (0,1)(1,1) :RE#11:E (.?)* x (0,1)(0,1) :RE#12:E (.?.?) xxx (0,2)(0,2) :RE#13:E (.?.?){1} xxx (0,2)(0,2) :RE#14:E (.?.?)(.?.?) xxx (0,3)(0,2)(2,3) :RE#15:E (.?.?){2} xxx (0,3)(2,3) :RE#16:E (.?.?)(.?.?)(.?.?) xxx (0,3)(0,2)(2,3)(3,3) :RE#17:E (.?.?){3} xxx (0,3)(3,3) :RE#18:E (.?.?)* xxx (0,3)(2,3) :RE#19:E a?((ab)?)(b?) ab (0,2)(1,1)(?,?)(1,2) :RE#20:E (a?)((ab)?)b? ab (0,2)(0,1)(1,1)(?,?) :RE#21:E a?((ab)?)b? ab (0,2)(1,1)(?,?) :RE#22:E (a*){2} xxxxx (0,0)(0,0) :RE#23:E (ab?)(b?a) aba (0,3)(0,2)(2,3) :RE#24:E (a|ab)(ba|a) aba (0,3)(0,2)(2,3) :RE#25:E (a|ab|ba) aba (0,2)(0,2) :RE#26:E (a|ab|ba)(a|ab|ba) aba (0,3)(0,2)(2,3) :RE#27:E (a|ab|ba)* aba (0,3)(2,3) :RE#28:E (aba|a*b) ababa (0,3)(0,3) :RE#29:E (aba|a*b)(aba|a*b) ababa (0,5)(0,2)(2,5) :RE#30:E (aba|a*b)* ababa (0,5)(2,5) :RE#31:E (aba|ab|a) ababa (0,3)(0,3) :RE#32:E (aba|ab|a)(aba|ab|a) ababa (0,5)(0,2)(2,5) :RE#33:E (aba|ab|a)* ababa (0,5)(2,5) :RE#34:E (a(b)?) aba (0,2)(0,2)(1,2) :RE#35:E (a(b)?)(a(b)?) aba (0,3)(0,2)(1,2)(2,3)(?,?) :RE#36:E (a(b)?)+ aba (0,3)(2,3)(?,?) :RE#37:E (.*)(.*) xx (0,2)(0,2)(2,2) :RE#38:E .*(.*) xx (0,2)(2,2) :RE#39:E (a.*z|b.*y) azbazby (0,5)(0,5) :RE#40:E (a.*z|b.*y)(a.*z|b.*y) azbazby (0,7)(0,5)(5,7) :RE#41:E (a.*z|b.*y)* azbazby (0,7)(5,7) :RE#42:E (.|..)(.*) ab (0,2)(0,2)(2,2) :RE#43:E ((..)*(...)*) xxx (0,3)(0,3)(?,?)(0,3) :RE#44:E ((..)*(...)*)((..)*(...)*) xxx (0,3)(0,3)(?,?)(0,3)(3,3)(?,?) :RE#45:E ((..)*(...)*)* xxx (0,3)(0,3)(?,?)(0,3) :RE#46:B \(a\{0,1\}\)*b\1 ab (0,2)(1,1) :RE#47:B \(a*\)*b\1 ab (0,2)(1,1) :RE#48:B \(a*\)b\1* ab (0,2)(0,1) :RE#49:B \(a*\)*b\1* ab (0,2)(1,1) :RE#50:B \(a\{0,1\}\)*b\(\1\) ab (0,2)(1,1)(2,2) :RE#51:B \(a*\)*b\(\1\) ab (0,2)(1,1)(2,2) :RE#52:B \(a*\)b\(\1\)* ab (0,2)(0,1)(?,?) :RE#53:B \(a*\)*b\(\1\)* ab (0,2)(1,1)(2,2) :RE#54:B \(a\{0,1\}\)*b\1 aba (0,3)(0,1) :RE#55:B \(a*\)*b\1 aba (0,3)(0,1) :RE#56:B \(a*\)b\1* aba (0,3)(0,1) :RE#57:B \(a*\)*b\1* aba (0,3)(0,1) :RE#58:B \(a*\)*b\(\1\)* aba (0,3)(0,1)(2,3) :RE#59:B \(a\{0,1\}\)*b\1 abaa (0,3)(0,1) :RE#60:B \(a*\)*b\1 abaa (0,3)(0,1) :RE#61:B \(a*\)b\1* abaa (0,4)(0,1) :RE#62:B \(a*\)*b\1* abaa (0,4)(0,1) :RE#63:B \(a*\)*b\(\1\)* abaa (0,4)(0,1)(3,4) :RE#64:B \(a\{0,1\}\)*b\1 aab (0,3)(2,2) :RE#65:B \(a*\)*b\1 aab (0,3)(2,2) :RE#66:B \(a*\)b\1* aab (0,3)(0,2) :RE#67:B \(a*\)*b\1* aab (0,3)(2,2) :RE#68:B \(a*\)*b\(\1\)* aab (0,3)(2,2)(3,3) :RE#69:B \(a\{0,1\}\)*b\1 aaba (0,4)(1,2) :RE#70:B \(a*\)*b\1 aaba (0,4)(1,2) :RE#71:B \(a*\)b\1* aaba (0,3)(0,2) :RE#72:B \(a*\)*b\1* aaba (0,4)(1,2) :RE#73:B \(a*\)*b\(\1\)* aaba (0,4)(1,2)(3,4) :RE#74:B \(a\{0,1\}\)*b\1 aabaa (0,4)(1,2) :RE#75:B \(a*\)*b\1 aabaa (0,5)(0,2) :RE#76:B \(a*\)b\1* aabaa (0,5)(0,2) :RE#77:B \(a*\)*b\1* aabaa (0,5)(0,2) :RE#78:B \(a*\)*b\(\1\)* aabaa (0,5)(0,2)(3,5) :RE#79:B \(x\)*a\1 a NOMATCH :RE#80:B \(x\)*a\1* a (0,1)(?,?) :RE#81:B \(x\)*a\(\1\) a NOMATCH :RE#82:B \(x\)*a\(\1\)* a (0,1)(?,?)(?,?) :RE#83:E (aa(b(b))?)+ aabbaa (0,6)(4,6)(?,?)(?,?) :RE#84:E (a(b)?)+ aba (0,3)(2,3)(?,?) :RE#85:E ([ab]+)([bc]+)([cd]*) abcd (0,4)(0,2)(2,3)(3,4) :RE#86:B \([ab]*\)\([bc]*\)\([cd]*\)\1 abcdaa (0,5)(0,1)(1,3)(3,4) :RE#87:B \([ab]*\)\([bc]*\)\([cd]*\)\1 abcdab (0,6)(0,2)(2,3)(3,4) :RE#88:B \([ab]*\)\([bc]*\)\([cd]*\)\1* abcdaa (0,6)(0,1)(1,3)(3,4) :RE#89:B \([ab]*\)\([bc]*\)\([cd]*\)\1* abcdab (0,6)(0,2)(2,3)(3,4) :RE#90:E ^(A([^B]*))?(B(.*))? Aa (0,2)(0,2)(1,2) :RE#91:E ^(A([^B]*))?(B(.*))? Bb (0,2)(?,?)(?,?)(0,2)(1,2) :RE#92:B .*\([AB]\).*\1 ABA (0,3)(0,1) :RE#93:B$ [^A]*A \nA (0,2) pgqd/lib/test/attregex/data/forcedassoc.dat0000664000401600040160000000241413175113172017323 0ustar cbecbeNOTE left-assoc:pass-all right-assoc:pass-all : 2002-04-29 E (a|ab)(c|bcd) abcd (0,4)(0,1)(1,4) E (a|ab)(bcd|c) abcd (0,4)(0,1)(1,4) E (ab|a)(c|bcd) abcd (0,4)(0,1)(1,4) E (ab|a)(bcd|c) abcd (0,4)(0,1)(1,4) E ((a|ab)(c|bcd))(d*) abcd (0,4)(0,4)(0,1)(1,4)(4,4) E ((a|ab)(bcd|c))(d*) abcd (0,4)(0,4)(0,1)(1,4)(4,4) E ((ab|a)(c|bcd))(d*) abcd (0,4)(0,4)(0,1)(1,4)(4,4) E ((ab|a)(bcd|c))(d*) abcd (0,4)(0,4)(0,1)(1,4)(4,4) E (a|ab)((c|bcd)(d*)) abcd (0,4)(0,2)(2,4)(2,3)(3,4) E (a|ab)((bcd|c)(d*)) abcd (0,4)(0,2)(2,4)(2,3)(3,4) E (ab|a)((c|bcd)(d*)) abcd (0,4)(0,2)(2,4)(2,3)(3,4) E (ab|a)((bcd|c)(d*)) abcd (0,4)(0,2)(2,4)(2,3)(3,4) E (a*)(b|abc) abc (0,3)(0,0)(0,3) E (a*)(abc|b) abc (0,3)(0,0)(0,3) E ((a*)(b|abc))(c*) abc (0,3)(0,3)(0,0)(0,3)(3,3) E ((a*)(abc|b))(c*) abc (0,3)(0,3)(0,0)(0,3)(3,3) E (a*)((b|abc)(c*)) abc (0,3)(0,1)(1,3)(1,2)(2,3) E (a*)((abc|b)(c*)) abc (0,3)(0,1)(1,3)(1,2)(2,3) E (a*)(b|abc) abc (0,3)(0,0)(0,3) E (a*)(abc|b) abc (0,3)(0,0)(0,3) E ((a*)(b|abc))(c*) abc (0,3)(0,3)(0,0)(0,3)(3,3) E ((a*)(abc|b))(c*) abc (0,3)(0,3)(0,0)(0,3)(3,3) E (a*)((b|abc)(c*)) abc (0,3)(0,1)(1,3)(1,2)(2,3) E (a*)((abc|b)(c*)) abc (0,3)(0,1)(1,3)(1,2)(2,3) E (a|ab) ab (0,2)(0,2) E (ab|a) ab (0,2)(0,2) E (a|ab)(b*) ab (0,2)(0,2)(2,2) E (ab|a)(b*) ab (0,2)(0,2)(2,2) pgqd/lib/test/attregex/testregex.c0000664000401600040160000014671713175113172015622 0ustar cbecbe#pragma prototyped noticed /* * regex(3) test harness * * build: cc -o testregex testregex.c * help: testregex --man * note: REG_* features are detected by #ifdef; if REG_* are enums * then supply #define REG_foo REG_foo for each enum REG_foo * * Glenn Fowler * AT&T Research * * PLEASE: publish your tests so everyone can benefit * * The following license covers testregex.c and all associated test data. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, and/or sell copies of the * Software, and to permit persons to whom the Software is furnished to do * so, subject to the following disclaimer: * * THIS SOFTWARE IS PROVIDED BY AT&T ``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 AT&T 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. */ static const char id[] = "\n@(#)$Id: testregex (AT&T Research) 2010-06-10 $\0\n"; #if _PACKAGE_ast #include #else #include #endif #include #include #include #include #include #include #ifdef __STDC__ #include #include #endif #define getline(x) xgetline(x) #include #ifdef USUAL #include #else #include #endif #ifndef RE_DUP_MAX #define RE_DUP_MAX 32767 #endif #if !_PACKAGE_ast #undef REG_DISCIPLINE #endif #ifndef REG_DELIMITED #undef _REG_subcomp #endif #define TEST_ARE 0x00000001 #define TEST_BRE 0x00000002 #define TEST_ERE 0x00000004 #define TEST_KRE 0x00000008 #define TEST_LRE 0x00000010 #define TEST_SRE 0x00000020 #define TEST_EXPAND 0x00000100 #define TEST_LENIENT 0x00000200 #define TEST_QUERY 0x00000400 #define TEST_SUB 0x00000800 #define TEST_UNSPECIFIED 0x00001000 #define TEST_VERIFY 0x00002000 #define TEST_AND 0x00004000 #define TEST_OR 0x00008000 #define TEST_DELIMIT 0x00010000 #define TEST_OK 0x00020000 #define TEST_SAME 0x00040000 #define TEST_ACTUAL 0x00100000 #define TEST_BASELINE 0x00200000 #define TEST_FAIL 0x00400000 #define TEST_PASS 0x00800000 #define TEST_SUMMARY 0x01000000 #define TEST_IGNORE_ERROR 0x02000000 #define TEST_IGNORE_OVER 0x04000000 #define TEST_IGNORE_POSITION 0x08000000 #define TEST_CATCH 0x10000000 #define TEST_VERBOSE 0x20000000 #define TEST_DECOMP 0x40000000 #define TEST_GLOBAL (TEST_ACTUAL|TEST_AND|TEST_BASELINE|TEST_CATCH|TEST_FAIL|TEST_IGNORE_ERROR|TEST_IGNORE_OVER|TEST_IGNORE_POSITION|TEST_OR|TEST_PASS|TEST_SUMMARY|TEST_VERBOSE) #ifdef REG_DISCIPLINE #include typedef struct Disc_s { regdisc_t disc; int ordinal; Sfio_t* sp; } Disc_t; static void* compf(const regex_t* re, const char* xstr, size_t xlen, regdisc_t* disc) { Disc_t* dp = (Disc_t*)disc; return (void*)((char*)0 + ++dp->ordinal); } static int execf(const regex_t* re, void* data, const char* xstr, size_t xlen, const char* sstr, size_t slen, char** snxt, regdisc_t* disc) { Disc_t* dp = (Disc_t*)disc; sfprintf(dp->sp, "{%-.*s}(%lu:%d)", xlen, xstr, (char*)data - (char*)0, slen); return atoi(xstr); } static void* resizef(void* handle, void* data, size_t size) { if (!size) return 0; return stkalloc((Sfio_t*)handle, size); } #endif #ifndef NiL #ifdef __STDC__ #define NiL 0 #else #define NiL (char*)0 #endif #endif #define H(x) do{if(html)fprintf(stderr,x);}while(0) #define T(x) fprintf(stderr,x) static void help(int html) { H("\n"); H("\n"); H("\n"); H("testregex man document\n"); H("\n"); H("\n"); H("
\n");
T("NAME\n");
T("  testregex - regex(3) test harness\n");
T("\n");
T("SYNOPSIS\n");
T("  testregex [ options ]\n");
T("\n");
T("DESCRIPTION\n");
T("  testregex reads regex(3) test specifications, one per line, from the\n");
T("  standard input and writes one output line for each failed test. A\n");
T("  summary line is written after all tests are done. Each successful\n");
T("  test is run again with REG_NOSUB. Unsupported features are noted\n");
T("  before the first test, and tests requiring these features are\n");
T("  silently ignored.\n");
T("\n");
T("OPTIONS\n");
T("  -c	catch signals and non-terminating calls\n");
T("  -e	ignore error return mismatches\n");
T("  -h	list help on standard error\n");
T("  -n	do not repeat successful tests with regnexec()\n");
T("  -o	ignore match[] overrun errors\n");
T("  -p	ignore negative position mismatches\n");
T("  -s	use stack instead of malloc\n");
T("  -x	do not repeat successful tests with REG_NOSUB\n");
T("  -v	list each test line\n");
T("  -A	list failed test lines with actual answers\n");
T("  -B	list all test lines with actual answers\n");
T("  -F	list failed test lines\n");
T("  -P	list passed test lines\n");
T("  -S	output one summary line\n");
T("\n");
T("INPUT FORMAT\n");
T("  Input lines may be blank, a comment beginning with #, or a test\n");
T("  specification. A specification is five fields separated by one\n");
T("  or more tabs. NULL denotes the empty string and NIL denotes the\n");
T("  0 pointer.\n");
T("\n");
T("  Field 1: the regex(3) flags to apply, one character per REG_feature\n");
T("  flag. The test is skipped if REG_feature is not supported by the\n");
T("  implementation. If the first character is not [BEASKLP] then the\n");
T("  specification is a global control line. One or more of [BEASKLP] may be\n");
T("  specified; the test will be repeated for each mode.\n");
T("\n");
T("    B 	basic			BRE	(grep, ed, sed)\n");
T("    E 	REG_EXTENDED		ERE	(egrep)\n");
T("    A	REG_AUGMENTED		ARE	(egrep with negation)\n");
T("    S	REG_SHELL		SRE	(sh glob)\n");
T("    K	REG_SHELL|REG_AUGMENTED	KRE	(ksh glob)\n");
T("    L	REG_LITERAL		LRE	(fgrep)\n");
T("\n");
T("    a	REG_LEFT|REG_RIGHT	implicit ^...$\n");
T("    b	REG_NOTBOL		lhs does not match ^\n");
T("    c	REG_COMMENT		ignore space and #...\\n\n");
T("    d	REG_SHELL_DOT		explicit leading . match\n");
T("    e	REG_NOTEOL		rhs does not match $\n");
T("    f	REG_MULTIPLE		multiple \\n separated patterns\n");
T("    g	FNM_LEADING_DIR		testfnmatch only -- match until /\n");
T("    h	REG_MULTIREF		multiple digit backref\n");
T("    i	REG_ICASE		ignore case\n");
T("    j	REG_SPAN		. matches \\n\n");
T("    k	REG_ESCAPE		\\ to ecape [...] delimiter\n");
T("    l	REG_LEFT		implicit ^...\n");
T("    m	REG_MINIMAL		minimal match\n");
T("    n	REG_NEWLINE		explicit \\n match\n");
T("    o	REG_ENCLOSED		(|&) magic inside [@|&](...)\n");
T("    p	REG_SHELL_PATH		explicit / match\n");
T("    q	REG_DELIMITED		delimited pattern\n");
T("    r	REG_RIGHT		implicit ...$\n");
T("    s	REG_SHELL_ESCAPED	\\ not special\n");
T("    t	REG_MUSTDELIM		all delimiters must be specified\n");
T("    u	standard unspecified behavior -- errors not counted\n");
T("    v	REG_CLASS_ESCAPE	\\ special inside [...]\n");
T("    w	REG_NOSUB		no subexpression match array\n");
T("    x	REG_LENIENT		let some errors slide\n");
T("    y	REG_LEFT		regexec() implicit ^...\n");
T("    z	REG_NULL		NULL subexpressions ok\n");
T("    $	                        expand C \\c escapes in fields 2 and 3\n");
T("    /	                        field 2 is a regsubcomp() expression\n");
T("    =	                        field 3 is a regdecomp() expression\n");
T("\n");
T("  Field 1 control lines:\n");
T("\n");
T("    C		set LC_COLLATE and LC_CTYPE to locale in field 2\n");
T("\n");
T("    ?test ...	output field 5 if passed and != EXPECTED, silent otherwise\n");
T("    &test ...	output field 5 if current and previous passed\n");
T("    |test ...	output field 5 if current passed and previous failed\n");
T("    ; ...	output field 2 if previous failed\n");
T("    {test ...	skip if failed until }\n");
T("    }		end of skip\n");
T("\n");
T("    : comment		comment copied as output NOTE\n");
T("    :comment:test	:comment: ignored\n");
T("    N[OTE] comment	comment copied as output NOTE\n");
T("    T[EST] comment	comment\n");
T("\n");
T("    number		use number for nmatch (20 by default)\n");
T("\n");
T("  Field 2: the regular expression pattern; SAME uses the pattern from\n");
T("    the previous specification. RE_DUP_MAX inside {...} expands to the\n");
T("    value from .\n");
T("\n");
T("  Field 3: the string to match. X...{RE_DUP_MAX} expands to RE_DUP_MAX\n");
T("    copies of X.\n");
T("\n");
T("  Field 4: the test outcome. This is either one of the posix error\n");
T("    codes (with REG_ omitted) or the match array, a list of (m,n)\n");
T("    entries with m and n being first and last+1 positions in the\n");
T("    field 3 string, or NULL if REG_NOSUB is in effect and success\n");
T("    is expected. BADPAT is acceptable in place of any regcomp(3)\n");
T("    error code. The match[] array is initialized to (-2,-2) before\n");
T("    each test. All array elements from 0 to nmatch-1 must be specified\n");
T("    in the outcome. Unspecified endpoints (offset -1) are denoted by ?.\n");
T("    Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a\n");
T("    matched (?{...}) expression, where x is the text enclosed by {...},\n");
T("    o is the expression ordinal counting from 1, and n is the length of\n");
T("    the unmatched portion of the subject string. If x starts with a\n");
T("    number then that is the return value of re_execf(), otherwise 0 is\n");
T("    returned. RE_DUP_MAX[-+]N expands to the  value -+N.\n");
T("\n");
T("  Field 5: optional comment appended to the report.\n");
T("\n");
T("CAVEAT\n");
T("    If a regex implementation misbehaves with memory then all bets are off.\n");
T("\n");
T("CONTRIBUTORS\n");
T("  Glenn Fowler    gsf@research.att.com        (ksh strmatch, regex extensions)\n");
T("  David Korn      dgk@research.att.com        (ksh glob matcher)\n");
T("  Doug McIlroy    mcilroy@dartmouth.edu       (ast regex/testre in C++)\n");
T("  Tom Lord        lord@regexps.com            (rx tests)\n");
T("  Henry Spencer   henry@zoo.toronto.edu       (original public regex)\n");
T("  Andrew Hume     andrew@research.att.com     (gre tests)\n");
T("  John Maddock    John_Maddock@compuserve.com (regex++ tests)\n");
T("  Philip Hazel    ph10@cam.ac.uk              (pcre tests)\n");
T("  Ville Laurikari vl@iki.fi                   (libtre tests)\n");
H("
\n"); H("\n"); H("\n"); } #ifndef elementsof #define elementsof(x) (sizeof(x)/sizeof(x[0])) #endif #ifndef streq #define streq(a,b) (*(a)==*(b)&&!strcmp(a,b)) #endif #define HUNG 2 #define NOTEST (~0) #ifndef REG_TEST_DEFAULT #define REG_TEST_DEFAULT 0 #endif #ifndef REG_EXEC_DEFAULT #define REG_EXEC_DEFAULT 0 #endif static const char* unsupported[] = { "BASIC", #ifndef REG_EXTENDED "EXTENDED", #endif #ifndef REG_AUGMENTED "AUGMENTED", #endif #ifndef REG_SHELL "SHELL", #endif #ifndef REG_CLASS_ESCAPE "CLASS_ESCAPE", #endif #ifndef REG_COMMENT "COMMENT", #endif #ifndef REG_DELIMITED "DELIMITED", #endif #ifndef REG_DISCIPLINE "DISCIPLINE", #endif #ifndef REG_ESCAPE "ESCAPE", #endif #ifndef REG_ICASE "ICASE", #endif #ifndef REG_LEFT "LEFT", #endif #ifndef REG_LENIENT "LENIENT", #endif #ifndef REG_LITERAL "LITERAL", #endif #ifndef REG_MINIMAL "MINIMAL", #endif #ifndef REG_MULTIPLE "MULTIPLE", #endif #ifndef REG_MULTIREF "MULTIREF", #endif #ifndef REG_MUSTDELIM "MUSTDELIM", #endif #ifndef REG_NEWLINE "NEWLINE", #endif #ifndef REG_NOTBOL "NOTBOL", #endif #ifndef REG_NOTEOL "NOTEOL", #endif #ifndef REG_NULL "NULL", #endif #ifndef REG_RIGHT "RIGHT", #endif #ifndef REG_SHELL_DOT "SHELL_DOT", #endif #ifndef REG_SHELL_ESCAPED "SHELL_ESCAPED", #endif #ifndef REG_SHELL_GROUP "SHELL_GROUP", #endif #ifndef REG_SHELL_PATH "SHELL_PATH", #endif #ifndef REG_SPAN "SPAN", #endif #if REG_NOSUB & REG_TEST_DEFAULT "SUBMATCH", #endif #if !_REG_nexec "regnexec", #endif #if !_REG_subcomp "regsubcomp", #endif #if !_REG_decomp "redecomp", #endif 0 }; #ifndef REG_CLASS_ESCAPE #define REG_CLASS_ESCAPE NOTEST #endif #ifndef REG_COMMENT #define REG_COMMENT NOTEST #endif #ifndef REG_DELIMITED #define REG_DELIMITED NOTEST #endif #ifndef REG_ESCAPE #define REG_ESCAPE NOTEST #endif #ifndef REG_ICASE #define REG_ICASE NOTEST #endif #ifndef REG_LEFT #define REG_LEFT NOTEST #endif #ifndef REG_LENIENT #define REG_LENIENT 0 #endif #ifndef REG_MINIMAL #define REG_MINIMAL NOTEST #endif #ifndef REG_MULTIPLE #define REG_MULTIPLE NOTEST #endif #ifndef REG_MULTIREF #define REG_MULTIREF NOTEST #endif #ifndef REG_MUSTDELIM #define REG_MUSTDELIM NOTEST #endif #ifndef REG_NEWLINE #define REG_NEWLINE NOTEST #endif #ifndef REG_NOTBOL #define REG_NOTBOL NOTEST #endif #ifndef REG_NOTEOL #define REG_NOTEOL NOTEST #endif #ifndef REG_NULL #define REG_NULL NOTEST #endif #ifndef REG_RIGHT #define REG_RIGHT NOTEST #endif #ifndef REG_SHELL_DOT #define REG_SHELL_DOT NOTEST #endif #ifndef REG_SHELL_ESCAPED #define REG_SHELL_ESCAPED NOTEST #endif #ifndef REG_SHELL_GROUP #define REG_SHELL_GROUP NOTEST #endif #ifndef REG_SHELL_PATH #define REG_SHELL_PATH NOTEST #endif #ifndef REG_SPAN #define REG_SPAN NOTEST #endif #define REG_UNKNOWN (-1) #ifndef REG_ENEWLINE #define REG_ENEWLINE (REG_UNKNOWN-1) #endif #ifndef REG_ENULL #ifndef REG_EMPTY #define REG_ENULL (REG_UNKNOWN-2) #else #define REG_ENULL REG_EMPTY #endif #endif #ifndef REG_ECOUNT #define REG_ECOUNT (REG_UNKNOWN-3) #endif #ifndef REG_BADESC #define REG_BADESC (REG_UNKNOWN-4) #endif #ifndef REG_EMEM #define REG_EMEM (REG_UNKNOWN-5) #endif #ifndef REG_EHUNG #define REG_EHUNG (REG_UNKNOWN-6) #endif #ifndef REG_EBUS #define REG_EBUS (REG_UNKNOWN-7) #endif #ifndef REG_EFAULT #define REG_EFAULT (REG_UNKNOWN-8) #endif #ifndef REG_EFLAGS #define REG_EFLAGS (REG_UNKNOWN-9) #endif #ifndef REG_EDELIM #define REG_EDELIM (REG_UNKNOWN-9) #endif static const struct { int code; char* name; } codes[] = { REG_UNKNOWN, "UNKNOWN", REG_NOMATCH, "NOMATCH", REG_BADPAT, "BADPAT", REG_ECOLLATE, "ECOLLATE", REG_ECTYPE, "ECTYPE", REG_EESCAPE, "EESCAPE", REG_ESUBREG, "ESUBREG", REG_EBRACK, "EBRACK", REG_EPAREN, "EPAREN", REG_EBRACE, "EBRACE", REG_BADBR, "BADBR", REG_ERANGE, "ERANGE", REG_ESPACE, "ESPACE", REG_BADRPT, "BADRPT", REG_ENEWLINE, "ENEWLINE", REG_ENULL, "ENULL", REG_ECOUNT, "ECOUNT", REG_BADESC, "BADESC", REG_EMEM, "EMEM", REG_EHUNG, "EHUNG", REG_EBUS, "EBUS", REG_EFAULT, "EFAULT", REG_EFLAGS, "EFLAGS", REG_EDELIM, "EDELIM", }; static struct { regmatch_t NOMATCH; int errors; int extracted; int ignored; int lineno; int passed; int signals; int unspecified; int verify; int warnings; char* file; char* stack; char* which; jmp_buf gotcha; #ifdef REG_DISCIPLINE Disc_t disc; #endif } state; static void quote(char* s, int len, unsigned long test) { unsigned char* u = (unsigned char*)s; unsigned char* e; int c; #ifdef MB_CUR_MAX int w; #endif if (!u) printf("NIL"); else if (!*u && len <= 1) printf("NULL"); else if (test & TEST_EXPAND) { if (len < 0) len = strlen((char*)u); e = u + len; if (test & TEST_DELIMIT) printf("\""); while (u < e) switch (c = *u++) { case '\\': printf("\\\\"); break; case '"': if (test & TEST_DELIMIT) printf("\\\""); else printf("\""); break; case '\a': printf("\\a"); break; case '\b': printf("\\b"); break; case 033: printf("\\e"); break; case '\f': printf("\\f"); break; case '\n': printf("\\n"); break; case '\r': printf("\\r"); break; case '\t': printf("\\t"); break; case '\v': printf("\\v"); break; default: #ifdef MB_CUR_MAX s = (char*)u - 1; if ((w = mblen(s, (char*)e - s)) > 1) { u += w - 1; fwrite(s, 1, w, stdout); } else #endif if (!iscntrl(c) && isprint(c)) putchar(c); else printf("\\x%02x", c); break; } if (test & TEST_DELIMIT) printf("\""); } else printf("%s", s); } static void report(char* comment, char* fun, char* re, char* s, int len, char* msg, int flags, unsigned long test) { if (state.file) printf("%s:", state.file); printf("%d:", state.lineno); if (re) { printf(" "); quote(re, -1, test|TEST_DELIMIT); if (s) { printf(" versus "); quote(s, len, test|TEST_DELIMIT); } } if (test & TEST_UNSPECIFIED) { state.unspecified++; printf(" unspecified behavior"); } else state.errors++; if (state.which) printf(" %s", state.which); if (flags & REG_NOSUB) printf(" NOSUB"); if (fun) printf(" %s", fun); if (comment[strlen(comment)-1] == '\n') printf(" %s", comment); else { printf(" %s: ", comment); if (msg) printf("%s: ", msg); } } static void error(regex_t* preg, int code) { char* msg; char buf[256]; switch (code) { case REG_EBUS: msg = "bus error"; break; case REG_EFAULT: msg = "memory fault"; break; case REG_EHUNG: msg = "did not terminate"; break; default: regerror(code, preg, msg = buf, sizeof buf); break; } printf("%s\n", msg); } static void bad(char* comment, char* re, char* s, int len, unsigned long test) { printf("bad test case "); report(comment, NiL, re, s, len, NiL, 0, test); exit(1); } static int escape(char* s) { char* b; char* t; char* q; char* e; int c; for (b = t = s; *t = *s; s++, t++) if (*s == '\\') switch (*++s) { case '\\': break; case 'a': *t = '\a'; break; case 'b': *t = '\b'; break; case 'c': if (*t = *++s) *t &= 037; else s--; break; case 'e': case 'E': *t = 033; break; case 'f': *t = '\f'; break; case 'n': *t = '\n'; break; case 'r': *t = '\r'; break; case 's': *t = ' '; break; case 't': *t = '\t'; break; case 'v': *t = '\v'; break; case 'u': case 'x': c = 0; q = c == 'u' ? (s + 5) : (char*)0; e = s + 1; while (!e || !q || s < q) { switch (*++s) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': c = (c << 4) + *s - 'a' + 10; continue; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': c = (c << 4) + *s - 'A' + 10; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': c = (c << 4) + *s - '0'; continue; case '{': case '[': if (s != e) { s--; break; } e = 0; continue; case '}': case ']': if (e) s--; break; default: s--; break; } break; } *t = c; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = *s - '0'; q = s + 2; while (s < q) { switch (*++s) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = (c << 3) + *s - '0'; break; default: q = --s; break; } } *t = c; break; default: *(s + 1) = 0; bad("invalid C \\ escape\n", s - 1, NiL, 0, 0); } return t - b; } static void matchoffprint(int off) { switch (off) { case -2: printf("X"); break; case -1: printf("?"); break; default: printf("%d", off); break; } } static void matchprint(regmatch_t* match, int nmatch, int nsub, char* ans, unsigned long test) { int i; for (; nmatch > nsub + 1; nmatch--) if ((match[nmatch-1].rm_so != -1 || match[nmatch-1].rm_eo != -1) && (!(test & TEST_IGNORE_POSITION) || match[nmatch-1].rm_so >= 0 && match[nmatch-1].rm_eo >= 0)) break; for (i = 0; i < nmatch; i++) { printf("("); matchoffprint(match[i].rm_so); printf(","); matchoffprint(match[i].rm_eo); printf(")"); } if (!(test & (TEST_ACTUAL|TEST_BASELINE))) { if (ans) printf(" expected: %s", ans); printf("\n"); } } static int matchcheck(regmatch_t* match, int nmatch, int nsub, char* ans, char* re, char* s, int len, int flags, unsigned long test) { char* p; int i; int m; int n; if (streq(ans, "OK")) return test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY); for (i = 0, p = ans; i < nmatch && *p; i++) { if (*p == '{') { #ifdef REG_DISCIPLINE char* x; if (!(x = sfstruse(state.disc.sp))) bad("out of space [discipline string]\n", NiL, NiL, 0, 0); if (strcmp(p, x)) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) return 0; report("callout failed", NiL, re, s, len, NiL, flags, test); quote(p, -1, test); printf(" expected, "); quote(x, -1, test); printf(" returned\n"); } #endif break; } if (*p++ != '(') bad("improper answer\n", re, s, -1, test); if (*p == '?') { m = -1; p++; } else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10)) { m = RE_DUP_MAX; p += 10; if (*p == '+' || *p == '-') m += strtol(p, &p, 10); } else m = strtol(p, &p, 10); if (*p++ != ',') bad("improper answer\n", re, s, -1, test); if (*p == '?') { n = -1; p++; } else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10)) { n = RE_DUP_MAX; p += 10; if (*p == '+' || *p == '-') n += strtol(p, &p, 10); } else n = strtol(p, &p, 10); if (*p++ != ')') bad("improper answer\n", re, s, -1, test); if (m!=match[i].rm_so || n!=match[i].rm_eo) { if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) { report("failed: match was", NiL, re, s, len, NiL, flags, test); matchprint(match, nmatch, nsub, ans, test); } return 0; } } for (; i < nmatch; i++) { if (match[i].rm_so!=-1 || match[i].rm_eo!=-1) { if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_VERIFY))) { if ((test & TEST_IGNORE_POSITION) && (match[i].rm_so<0 || match[i].rm_eo<0)) { state.ignored++; return 0; } if (!(test & TEST_SUMMARY)) { report("failed: match was", NiL, re, s, len, NiL, flags, test); matchprint(match, nmatch, nsub, ans, test); } } return 0; } } if (!(test & TEST_IGNORE_OVER) && match[nmatch].rm_so != state.NOMATCH.rm_so) { if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) { report("failed: overran match array", NiL, re, s, len, NiL, flags, test); matchprint(match, nmatch + 1, nsub, NiL, test); } return 0; } return 1; } static void sigunblock(int s) { #ifdef SIG_SETMASK int op; sigset_t mask; sigemptyset(&mask); if (s) { sigaddset(&mask, s); op = SIG_UNBLOCK; } else op = SIG_SETMASK; sigprocmask(op, &mask, NiL); #else #ifdef sigmask sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L); #endif #endif } static void gotcha(int sig) { int ret; signal(sig, gotcha); alarm(0); state.signals++; switch (sig) { case SIGALRM: ret = REG_EHUNG; break; case SIGBUS: ret = REG_EBUS; break; default: ret = REG_EFAULT; break; } sigunblock(sig); longjmp(state.gotcha, ret); } static char* getline(FILE* fp) { static char buf[32 * 1024]; register char* s = buf; register char* e = &buf[sizeof(buf)]; register char* b; for (;;) { if (!(b = fgets(s, e - s, fp))) return 0; state.lineno++; s += strlen(s); if (s == b || *--s != '\n' || s == b || *(s - 1) != '\\') { *s = 0; break; } s--; } return buf; } static unsigned long note(unsigned long level, char* msg, unsigned long skip, unsigned long test) { if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY)) && !skip) { printf("NOTE\t"); if (msg) printf("%s: ", msg); printf("skipping lines %d", state.lineno); } return skip | level; } #define TABS(n) &ts[7-((n)&7)] static char ts[] = "\t\t\t\t\t\t\t"; static unsigned long extract(int* tabs, char* spec, char* re, char* s, char* ans, char* msg, char* accept, regmatch_t* match, int nmatch, int nsub, unsigned long skip, unsigned long level, unsigned long test) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_OK|TEST_PASS|TEST_SUMMARY)) { state.extracted = 1; if (test & TEST_OK) { state.passed++; if ((test & TEST_VERIFY) && !(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) { if (msg && strcmp(msg, "EXPECTED")) printf("NOTE\t%s\n", msg); return skip; } test &= ~(TEST_PASS|TEST_QUERY); } if (test & (TEST_QUERY|TEST_VERIFY)) { if (test & TEST_BASELINE) test &= ~(TEST_BASELINE|TEST_PASS); else test |= TEST_PASS; skip |= level; } if (!(test & TEST_OK)) { if (test & TEST_UNSPECIFIED) state.unspecified++; else state.errors++; } if (test & (TEST_PASS|TEST_SUMMARY)) return skip; test &= ~TEST_DELIMIT; printf("%s%s", spec, TABS(*tabs++)); if ((test & (TEST_BASELINE|TEST_SAME)) == (TEST_BASELINE|TEST_SAME)) printf("SAME"); else quote(re, -1, test); printf("%s", TABS(*tabs++)); quote(s, -1, test); printf("%s", TABS(*tabs++)); if (!(test & (TEST_ACTUAL|TEST_BASELINE)) || !accept && !match) printf("%s", ans); else if (accept) printf("%s", accept); else matchprint(match, nmatch, nsub, NiL, test); if (msg) printf("%s%s", TABS(*tabs++), msg); putchar('\n'); } else if (test & TEST_QUERY) skip = note(level, msg, skip, test); else if (test & TEST_VERIFY) state.extracted = 1; return skip; } static int catchfree(regex_t* preg, int flags, int* tabs, char* spec, char* re, char* s, char* ans, char* msg, char* accept, regmatch_t* match, int nmatch, int nsub, unsigned long skip, unsigned long level, unsigned long test) { int eret; if (!(test & TEST_CATCH)) { regfree(preg); eret = 0; } else if (!(eret = setjmp(state.gotcha))) { alarm(HUNG); regfree(preg); alarm(0); } else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) extract(tabs, spec, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); else { report("failed", "regfree", re, NiL, -1, msg, flags, test); error(preg, eret); } return eret; } static char* expand(char* os, char* ot) { char* s = os; char* t; int n = 0; int r; long m; for (;;) { switch (*s++) { case 0: break; case '{': n++; continue; case '}': n--; continue; case 'R': if (n == 1 && !memcmp(s, "E_DUP_MAX", 9)) { s--; for (t = ot; os < s; *t++ = *os++); r = ((t - ot) >= 5 && t[-1] == '{' && t[-2] == '.' && t[-3] == '.' && t[-4] == '.') ? t[-5] : 0; os = ot; m = RE_DUP_MAX; if (*(s += 10) == '+' || *s == '-') m += strtol(s, &s, 10); if (r) { t -= 5; while (m-- > 0) *t++ = r; while (*s && *s++ != '}'); } else t += snprintf(t, 32, "%ld", m); while (*t = *s++) t++; break; } continue; default: continue; } break; } return os; } int main(int argc, char** argv) { int flags; int cflags; int eflags; int nmatch; int nexec; int nstr; int cret; int eret; int nsub; int i; int j; int expected; int got; int locale; int subunitlen; int testno; unsigned long level; unsigned long skip; char* p; char* line; char* spec; char* re; char* s; char* ans; char* msg; char* fun; char* ppat; char* subunit; char* version; char* field[6]; char* delim[6]; FILE* fp; int tabs[6]; char unit[64]; regmatch_t match[100]; regex_t preg; static char pat[32 * 1024]; static char patbuf[32 * 1024]; static char strbuf[32 * 1024]; int nonosub = REG_NOSUB == 0; int nonexec = 0; unsigned long test = 0; static char* filter[] = { "-", 0 }; state.NOMATCH.rm_so = state.NOMATCH.rm_eo = -2; p = unit; version = (char*)id + 10; while (p < &unit[sizeof(unit)-1] && (*p = *version++) && !isspace(*p)) p++; *p = 0; while ((p = *++argv) && *p == '-') for (;;) { switch (*++p) { case 0: break; case 'c': test |= TEST_CATCH; continue; case 'e': test |= TEST_IGNORE_ERROR; continue; case 'h': case '?': help(0); return 2; case '-': help(p[1] == 'h'); return 2; case 'n': nonexec = 1; continue; case 'o': test |= TEST_IGNORE_OVER; continue; case 'p': test |= TEST_IGNORE_POSITION; continue; case 's': #ifdef REG_DISCIPLINE if (!(state.stack = stkalloc(stkstd, 0))) fprintf(stderr, "%s: out of space [stack]", unit); state.disc.disc.re_resizef = resizef; state.disc.disc.re_resizehandle = (void*)stkstd; #endif continue; case 'x': nonosub = 1; continue; case 'v': test |= TEST_VERBOSE; continue; case 'A': test |= TEST_ACTUAL; continue; case 'B': test |= TEST_BASELINE; continue; case 'F': test |= TEST_FAIL; continue; case 'P': test |= TEST_PASS; continue; case 'S': test |= TEST_SUMMARY; continue; default: fprintf(stderr, "%s: %c: invalid option\n", unit, *p); return 2; } break; } if (!*argv) argv = filter; locale = 0; while (state.file = *argv++) { if (streq(state.file, "-") || streq(state.file, "/dev/stdin") || streq(state.file, "/dev/fd/0")) { state.file = 0; fp = stdin; } else if (!(fp = fopen(state.file, "r"))) { fprintf(stderr, "%s: %s: cannot read\n", unit, state.file); return 2; } testno = state.errors = state.ignored = state.lineno = state.passed = state.signals = state.unspecified = state.warnings = 0; skip = 0; level = 1; if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) { printf("TEST\t%s ", unit); if (s = state.file) { subunit = p = 0; for (;;) { switch (*s++) { case 0: break; case '/': subunit = s; continue; case '.': p = s - 1; continue; default: continue; } break; } if (!subunit) subunit = state.file; if (p < subunit) p = s - 1; subunitlen = p - subunit; printf("%-.*s ", subunitlen, subunit); } else subunit = 0; for (s = version; *s && (*s != ' ' || *(s + 1) != '$'); s++) putchar(*s); if (test & TEST_CATCH) printf(", catch"); if (test & TEST_IGNORE_ERROR) printf(", ignore error code mismatches"); if (test & TEST_IGNORE_POSITION) printf(", ignore negative position mismatches"); #ifdef REG_DISCIPLINE if (state.stack) printf(", stack"); #endif if (test & TEST_VERBOSE) printf(", verbose"); printf("\n"); #ifdef REG_VERSIONID if (regerror(REG_VERSIONID, NiL, pat, sizeof(pat)) > 0) s = pat; else #endif #ifdef REG_TEST_VERSION s = REG_TEST_VERSION; #else s = "regex"; #endif printf("NOTE\t%s\n", s); if (elementsof(unsupported) > 1) { #if (REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) || !defined(REG_EXTENDED) i = 0; #else i = REG_EXTENDED != 0; #endif for (got = 0; i < elementsof(unsupported) - 1; i++) { if (!got) { got = 1; printf("NOTE\tunsupported: %s", unsupported[i]); } else printf(",%s", unsupported[i]); } if (got) printf("\n"); } } #ifdef REG_DISCIPLINE state.disc.disc.re_version = REG_VERSION; state.disc.disc.re_compf = compf; state.disc.disc.re_execf = execf; if (!(state.disc.sp = sfstropen())) bad("out of space [discipline string stream]\n", NiL, NiL, 0, 0); preg.re_disc = &state.disc.disc; #endif if (test & TEST_CATCH) { signal(SIGALRM, gotcha); signal(SIGBUS, gotcha); signal(SIGSEGV, gotcha); } while (p = getline(fp)) { /* parse: */ line = p; if (*p == ':' && !isspace(*(p + 1))) { while (*++p && *p != ':'); if (!*p++) { if (test & TEST_BASELINE) printf("%s\n", line); continue; } } while (isspace(*p)) p++; if (*p == 0 || *p == '#' || *p == 'T') { if (test & TEST_BASELINE) printf("%s\n", line); continue; } if (*p == ':' || *p == 'N') { if (test & TEST_BASELINE) printf("%s\n", line); else if (!(test & (TEST_ACTUAL|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) { while (*++p && !isspace(*p)); while (isspace(*p)) p++; printf("NOTE %s\n", p); } continue; } j = 0; i = 0; field[i++] = p; for (;;) { switch (*p++) { case 0: p--; j = 0; goto checkfield; case '\t': *(delim[i] = p - 1) = 0; j = 1; checkfield: s = field[i - 1]; if (streq(s, "NIL")) field[i - 1] = 0; else if (streq(s, "NULL")) *s = 0; while (*p == '\t') { p++; j++; } tabs[i - 1] = j; if (!*p) break; if (i >= elementsof(field)) bad("too many fields\n", NiL, NiL, 0, 0); field[i++] = p; /*FALLTHROUGH*/ default: continue; } break; } if (!(spec = field[0])) bad("NIL spec\n", NiL, NiL, 0, 0); /* interpret: */ cflags = REG_TEST_DEFAULT; eflags = REG_EXEC_DEFAULT; test &= TEST_GLOBAL; state.extracted = 0; nmatch = 20; nsub = -1; for (p = spec; *p; p++) { if (isdigit(*p)) { nmatch = strtol(p, &p, 10); if (nmatch >= elementsof(match)) bad("nmatch must be < 100\n", NiL, NiL, 0, 0); p--; continue; } switch (*p) { case 'A': test |= TEST_ARE; continue; case 'B': test |= TEST_BRE; continue; case 'C': if (!(test & TEST_QUERY) && !(skip & level)) bad("locale must be nested\n", NiL, NiL, 0, 0); test &= ~TEST_QUERY; if (locale) bad("locale nesting not supported\n", NiL, NiL, 0, 0); if (i != 2) bad("locale field expected\n", NiL, NiL, 0, 0); if (!(skip & level)) { #if defined(LC_COLLATE) && defined(LC_CTYPE) s = field[1]; if (!s || streq(s, "POSIX")) s = "C"; if ((ans = setlocale(LC_COLLATE, s)) && streq(ans, "POSIX")) ans = "C"; if (!ans || !streq(ans, s) && streq(s, "C")) ans = 0; else if ((ans = setlocale(LC_CTYPE, s)) && streq(ans, "POSIX")) ans = "C"; if (!ans || !streq(ans, s) && streq(s, "C")) skip = note(level, s, skip, test); else { if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) printf("NOTE \"%s\" locale\n", s); locale = level; } #else skip = note(level, skip, test, "locales not supported"); #endif } cflags = NOTEST; continue; case 'E': test |= TEST_ERE; continue; case 'K': test |= TEST_KRE; continue; case 'L': test |= TEST_LRE; continue; case 'S': test |= TEST_SRE; continue; case 'a': cflags |= REG_LEFT|REG_RIGHT; continue; case 'b': eflags |= REG_NOTBOL; continue; case 'c': cflags |= REG_COMMENT; continue; case 'd': cflags |= REG_SHELL_DOT; continue; case 'e': eflags |= REG_NOTEOL; continue; case 'f': cflags |= REG_MULTIPLE; continue; case 'g': cflags |= NOTEST; continue; case 'h': cflags |= REG_MULTIREF; continue; case 'i': cflags |= REG_ICASE; continue; case 'j': cflags |= REG_SPAN; continue; case 'k': cflags |= REG_ESCAPE; continue; case 'l': cflags |= REG_LEFT; continue; case 'm': cflags |= REG_MINIMAL; continue; case 'n': cflags |= REG_NEWLINE; continue; case 'o': cflags |= REG_SHELL_GROUP; continue; case 'p': cflags |= REG_SHELL_PATH; continue; case 'q': cflags |= REG_DELIMITED; continue; case 'r': cflags |= REG_RIGHT; continue; case 's': cflags |= REG_SHELL_ESCAPED; continue; case 't': cflags |= REG_MUSTDELIM; continue; case 'u': test |= TEST_UNSPECIFIED; continue; case 'v': cflags |= REG_CLASS_ESCAPE; continue; case 'w': cflags |= REG_NOSUB; continue; case 'x': if (REG_LENIENT) cflags |= REG_LENIENT; else test |= TEST_LENIENT; continue; case 'y': eflags |= REG_LEFT; continue; case 'z': cflags |= REG_NULL; continue; case '$': test |= TEST_EXPAND; continue; case '/': test |= TEST_SUB; continue; case '=': test |= TEST_DECOMP; continue; case '?': test |= TEST_VERIFY; test &= ~(TEST_AND|TEST_OR); state.verify = state.passed; continue; case '&': test |= TEST_VERIFY|TEST_AND; test &= ~TEST_OR; continue; case '|': test |= TEST_VERIFY|TEST_OR; test &= ~TEST_AND; continue; case ';': test |= TEST_OR; test &= ~TEST_AND; continue; case '{': level <<= 1; if (skip & (level >> 1)) { skip |= level; cflags = NOTEST; } else { skip &= ~level; test |= TEST_QUERY; } continue; case '}': if (level == 1) bad("invalid {...} nesting\n", NiL, NiL, 0, 0); if ((skip & level) && !(skip & (level>>1))) { if (!(test & (TEST_BASELINE|TEST_SUMMARY))) { if (test & (TEST_ACTUAL|TEST_FAIL)) printf("}\n"); else if (!(test & TEST_PASS)) printf("-%d\n", state.lineno); } } #if defined(LC_COLLATE) && defined(LC_CTYPE) else if (locale & level) { locale = 0; if (!(skip & level)) { s = "C"; setlocale(LC_COLLATE, s); setlocale(LC_CTYPE, s); if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) printf("NOTE \"%s\" locale\n", s); else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_PASS)) printf("}\n"); } else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL)) printf("}\n"); } #endif level >>= 1; cflags = NOTEST; continue; default: bad("bad spec\n", spec, NiL, 0, test); break; } break; } if ((cflags|eflags) == NOTEST || (skip & level) && (test & TEST_BASELINE)) { if (test & TEST_BASELINE) { while (i > 1) *delim[--i] = '\t'; printf("%s\n", line); } continue; } if (test & TEST_OR) { if (!(test & TEST_VERIFY)) { test &= ~TEST_OR; if (state.passed == state.verify && i > 1) printf("NOTE\t%s\n", field[1]); continue; } else if (state.passed > state.verify) continue; } else if (test & TEST_AND) { if (state.passed == state.verify) continue; state.passed = state.verify; } if (i < ((test & TEST_DECOMP) ? 3 : 4)) bad("too few fields\n", NiL, NiL, 0, test); while (i < elementsof(field)) field[i++] = 0; if (re = field[1]) { if (streq(re, "SAME")) { re = ppat; test |= TEST_SAME; } else { if (test & TEST_EXPAND) escape(re); re = expand(re, patbuf); strcpy(ppat = pat, re); } } else ppat = 0; nstr = -1; if (s = field[2]) { s = expand(s, strbuf); if (test & TEST_EXPAND) { nstr = escape(s); #if _REG_nexec if (nstr != strlen(s)) nexec = nstr; #endif } } if (!(ans = field[(test & TEST_DECOMP) ? 2 : 3])) bad("NIL answer\n", NiL, NiL, 0, test); msg = field[4]; fflush(stdout); if (test & TEST_SUB) #if _REG_subcomp cflags |= REG_DELIMITED; #else continue; #endif #if !_REG_decomp if (test & TEST_DECOMP) continue; #endif compile: if (state.extracted || (skip & level)) continue; #if !(REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) #ifdef REG_EXTENDED if (REG_EXTENDED != 0 && (test & TEST_BRE)) #else if (test & TEST_BRE) #endif { test &= ~TEST_BRE; flags = cflags; state.which = "BRE"; } else #endif #ifdef REG_EXTENDED if (test & TEST_ERE) { test &= ~TEST_ERE; flags = cflags | REG_EXTENDED; state.which = "ERE"; } else #endif #ifdef REG_AUGMENTED if (test & TEST_ARE) { test &= ~TEST_ARE; flags = cflags | REG_AUGMENTED; state.which = "ARE"; } else #endif #ifdef REG_LITERAL if (test & TEST_LRE) { test &= ~TEST_LRE; flags = cflags | REG_LITERAL; state.which = "LRE"; } else #endif #ifdef REG_SHELL if (test & TEST_SRE) { test &= ~TEST_SRE; flags = cflags | REG_SHELL; state.which = "SRE"; } else #ifdef REG_AUGMENTED if (test & TEST_KRE) { test &= ~TEST_KRE; flags = cflags | REG_SHELL | REG_AUGMENTED; state.which = "KRE"; } else #endif #endif { if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test|TEST_OK); continue; } if ((test & (TEST_QUERY|TEST_VERBOSE|TEST_VERIFY)) == TEST_VERBOSE) { printf("test %-3d %s ", state.lineno, state.which); quote(re, -1, test|TEST_DELIMIT); printf(" "); quote(s, nstr, test|TEST_DELIMIT); printf("\n"); } nosub: fun = "regcomp"; #if _REG_nexec if (nstr >= 0 && nstr != strlen(s)) nexec = nstr; else #endif nexec = -1; if (state.extracted || (skip & level)) continue; if (!(test & TEST_QUERY)) testno++; #ifdef REG_DISCIPLINE if (state.stack) stkset(stkstd, state.stack, 0); flags |= REG_DISCIPLINE; state.disc.ordinal = 0; sfstrseek(state.disc.sp, 0, SEEK_SET); #endif if (!(test & TEST_CATCH)) cret = regcomp(&preg, re, flags); else if (!(cret = setjmp(state.gotcha))) { alarm(HUNG); cret = regcomp(&preg, re, flags); alarm(0); } #if _REG_subcomp if (!cret && (test & TEST_SUB)) { fun = "regsubcomp"; p = re + preg.re_npat; if (!(test & TEST_CATCH)) cret = regsubcomp(&preg, p, NiL, 0, 0); else if (!(cret = setjmp(state.gotcha))) { alarm(HUNG); cret = regsubcomp(&preg, p, NiL, 0, 0); alarm(0); } if (!cret && *(p += preg.re_npat) && !(preg.re_sub->re_flags & REG_SUB_LAST)) { if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) continue; cret = REG_EFLAGS; } } #endif #if _REG_decomp if (!cret && (test & TEST_DECOMP)) { char buf[128]; if ((j = nmatch) > sizeof(buf)) j = sizeof(buf); fun = "regdecomp"; p = re + preg.re_npat; if (!(test & TEST_CATCH)) i = regdecomp(&preg, -1, buf, j); else if (!(cret = setjmp(state.gotcha))) { alarm(HUNG); i = regdecomp(&preg, -1, buf, j); alarm(0); } if (!cret) { catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); if (i > j) { if (i != (strlen(ans) + 1)) { report("failed", fun, re, s, nstr, msg, flags, test); printf(" %d byte buffer supplied, %d byte buffer required\n", j, i); } } else if (strcmp(buf, ans)) { report("failed", fun, re, s, nstr, msg, flags, test); quote(ans, -1, test|TEST_DELIMIT); printf(" expected, "); quote(buf, -1, test|TEST_DELIMIT); printf(" returned\n"); } continue; } } #endif if (!cret) { if (!(flags & REG_NOSUB) && nsub < 0 && *ans == '(') { for (p = ans; *p; p++) if (*p == '(') nsub++; else if (*p == '{') nsub--; if (nsub >= 0) { if (test & TEST_IGNORE_OVER) { if (nmatch > nsub) nmatch = nsub + 1; } else if (nsub != preg.re_nsub) { if (nsub > preg.re_nsub) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); else { report("re_nsub incorrect", fun, re, NiL, -1, msg, flags, test); printf("at least %d expected, %d returned\n", nsub, (int)preg.re_nsub); state.errors++; } } else nsub = preg.re_nsub; } } } if (!(test & (TEST_DECOMP|TEST_SUB)) && *ans && *ans != '(' && !streq(ans, "OK") && !streq(ans, "NOMATCH")) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); else if (!(test & TEST_LENIENT)) { report("failed", fun, re, NiL, -1, msg, flags, test); printf("%s expected, OK returned\n", ans); } catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); continue; } } else { if (test & TEST_LENIENT) /* we'll let it go this time */; else if (!*ans || ans[0]=='(' || cret == REG_BADPAT && streq(ans, "NOMATCH")) { got = 0; for (i = 1; i < elementsof(codes); i++) if (cret==codes[i].code) got = i; if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); else { report("failed", fun, re, NiL, -1, msg, flags, test); printf("%s returned: ", codes[got].name); error(&preg, cret); } } else { expected = got = 0; for (i = 1; i < elementsof(codes); i++) { if (streq(ans, codes[i].name)) expected = i; if (cret==codes[i].code) got = i; } if (!expected) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); else { report("failed: invalid error code", NiL, re, NiL, -1, msg, flags, test); printf("%s expected, %s returned\n", ans, codes[got].name); } } else if (cret != codes[expected].code && cret != REG_BADPAT) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); else if (test & TEST_IGNORE_ERROR) state.ignored++; else { report("should fail and did", fun, re, NiL, -1, msg, flags, test); printf("%s expected, %s returned: ", ans, codes[got].name); state.errors--; state.warnings++; error(&preg, cret); } } } goto compile; } #if _REG_nexec execute: if (nexec >= 0) fun = "regnexec"; else #endif fun = "regexec"; for (i = 0; i < elementsof(match); i++) match[i] = state.NOMATCH; #if _REG_nexec if (nexec >= 0) { eret = regnexec(&preg, s, nexec, nmatch, match, eflags); s[nexec] = 0; } else #endif { if (!(test & TEST_CATCH)) eret = regexec(&preg, s, nmatch, match, eflags); else if (!(eret = setjmp(state.gotcha))) { alarm(HUNG); eret = regexec(&preg, s, nmatch, match, eflags); alarm(0); } } #if _REG_subcomp if ((test & TEST_SUB) && !eret) { fun = "regsubexec"; if (!(test & TEST_CATCH)) eret = regsubexec(&preg, s, nmatch, match); else if (!(eret = setjmp(state.gotcha))) { alarm(HUNG); eret = regsubexec(&preg, s, nmatch, match); alarm(0); } } #endif if (flags & REG_NOSUB) { if (eret) { if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, 0, skip, level, test|TEST_DELIMIT); else { report("REG_NOSUB failed", fun, re, s, nstr, msg, flags, test); error(&preg, eret); } } } else if (streq(ans, "NOMATCH")) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); else { report("should fail and didn't", fun, re, s, nstr, msg, flags, test); error(&preg, eret); } } } else if (eret) { if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, nsub, skip, level, test|TEST_DELIMIT); else { report("failed", fun, re, s, nstr, msg, flags, test); if (eret != REG_NOMATCH) error(&preg, eret); else if (*ans) printf("expected: %s\n", ans); else printf("\n"); } } } else if (streq(ans, "NOMATCH")) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); else { report("should fail and didn't", fun, re, s, nstr, msg, flags, test); matchprint(match, nmatch, nsub, NiL, test); } } #if _REG_subcomp else if (test & TEST_SUB) { p = preg.re_sub->re_buf; if (strcmp(p, ans)) { report("failed", fun, re, s, nstr, msg, flags, test); quote(ans, -1, test|TEST_DELIMIT); printf(" expected, "); quote(p, -1, test|TEST_DELIMIT); printf(" returned\n"); } } #endif else if (!*ans) { if (match[0].rm_so != state.NOMATCH.rm_so) { if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); else { report("failed: no match but match array assigned", NiL, re, s, nstr, msg, flags, test); matchprint(match, nmatch, nsub, NiL, test); } } } else if (matchcheck(match, nmatch, nsub, ans, re, s, nstr, flags, test)) { #if _REG_nexec if (nexec < 0 && !nonexec) { nexec = nstr >= 0 ? nstr : strlen(s); s[nexec] = '\n'; testno++; goto execute; } #endif if (!(test & (TEST_DECOMP|TEST_SUB|TEST_VERIFY)) && !nonosub) { if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) continue; flags |= REG_NOSUB; goto nosub; } if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_OK); } else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) continue; goto compile; } if (test & TEST_SUMMARY) printf("tests=%-4d errors=%-4d warnings=%-2d ignored=%-2d unspecified=%-2d signals=%d\n", testno, state.errors, state.warnings, state.ignored, state.unspecified, state.signals); else if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS))) { printf("TEST\t%s", unit); if (subunit) printf(" %-.*s", subunitlen, subunit); printf(", %d test%s", testno, testno == 1 ? "" : "s"); if (state.ignored) printf(", %d ignored mismatche%s", state.ignored, state.ignored == 1 ? "" : "s"); if (state.warnings) printf(", %d warning%s", state.warnings, state.warnings == 1 ? "" : "s"); if (state.unspecified) printf(", %d unspecified difference%s", state.unspecified, state.unspecified == 1 ? "" : "s"); if (state.signals) printf(", %d signal%s", state.signals, state.signals == 1 ? "" : "s"); printf(", %d error%s\n", state.errors, state.errors == 1 ? "" : "s"); } if (fp != stdin) fclose(fp); } return 0; } pgqd/lib/test/attregex/Makefile0000664000401600040160000000115613175113172015067 0ustar cbecbe SUBLOC = test/attregex AM_FEATURES = libusual AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) VPATH = $(top_srcdir) USUAL_DIR = $(top_srcdir) EXTRA_PROGRAMS = testregex.usual testregex.libc testregex_usual_SOURCES = testregex.c testregex_usual_DEFS = -DUSUAL -DUSE_INTERNAL_REGEX testregex_usual_EMBED_LIBUSUAL = 1 testregex_libc_SOURCES = testregex.c EXTRA_DIST = Makefile run.sh \ data/basic.dat \ data/categorize.dat \ data/forcedassoc.dat \ data/interpretation.dat \ data/leftassoc.dat \ data/nullsubexpr.dat \ data/repetition.dat \ data/rightassoc.dat test: $(EXTRA_PROGRAMS) include ../../build.mk pgqd/lib/test/test_common.c0000664000401600040160000000264213175113172014300 0ustar cbecbe #include "test_common.h" #include #include #include unsigned long long test_seed1, test_seed2; struct testgroup_t groups[] = { { "aatree/", aatree_tests }, { "base/", base_tests }, { "bits/", bits_tests }, { "cbtree/", cbtree_tests }, { "cfparser/", cfparser_tests }, { "crypto/", crypto_tests }, { "ctype/", ctype_tests }, { "cxalloc/", cxalloc_tests }, { "endian/", endian_tests }, { "event/", event_tests }, { "fileutil/", fileutil_tests }, { "fnmatch/", fnmatch_tests }, { "getopt/", getopt_tests }, { "hashing/", hashing_tests }, { "hashtab/", hashtab_tests }, { "heap/", heap_tests }, { "json/", json_tests }, { "list/", list_tests }, { "mdict/", mdict_tests }, { "netdb/", netdb_tests }, { "pgutil/", pgutil_tests }, { "psrandom/", psrandom_tests }, { "regex/", regex_tests }, { "shlist/", shlist_tests }, { "socket/", socket_tests }, { "string/", string_tests }, { "strpool/", strpool_tests }, { "talloc/", talloc_tests }, { "time/", time_tests }, { "tls/", tls_tests }, { "utf8/", utf8_tests }, { "wchar/", wchar_tests }, END_OF_GROUPS }; int main(int argc, const char *argv[]) { if (getenv("USE_LOCALE")) setlocale(LC_ALL, ""); test_seed1 = pseudo_random(); test_seed2 = pseudo_random(); pseudo_random_seed(test_seed1, test_seed2); printf("inital seed: %llu %llu\n", test_seed1, test_seed2); return tinytest_main(argc, argv, groups); } pgqd/lib/test/test_event.c0000664000401600040160000000222413175113172014125 0ustar cbecbe#include #include #include #include "test_common.h" static int ev_count; static void bumper(int fd, short flags, void *arg) { int res; int *other = arg; char buf[10]; ev_count++; res = recv(fd, buf, 10, 0); if (res != 1) { fprintf(stderr, "recv: %s\n", strerror(errno)); event_loopbreak(); } buf[0]++; if (buf[0] > 'z') { event_loopbreak(); } else { send(*other, buf, 1, 0); } } static void test_event(void *z) { struct event_base *base = NULL; int spair[2]; struct event ev[2]; base = event_init(); tt_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, spair) == 0); tt_assert(socket_setup(spair[0], true)); tt_assert(socket_setup(spair[1], true)); event_set(&ev[0], spair[0], EV_READ | EV_PERSIST, bumper, &spair[1]); tt_assert(event_add(&ev[0], NULL) == 0); event_set(&ev[1], spair[1], EV_READ | EV_PERSIST, bumper, &spair[0]); tt_assert(event_add(&ev[1], NULL) == 0); tt_assert(send(spair[0], "0", 1, 0) == 1); tt_assert(event_loop(0) == 0); event_base_free(base); int_check(ev_count, 75); end:; } struct testcase_t event_tests[] = { { "event", test_event }, END_OF_TESTCASES }; pgqd/lib/test/compile.c0000664000401600040160000000301313175113172013372 0ustar cbecbe#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool heap_is_better(const void *a, const void *b) { return 1; } int main(void) { struct AATree aatree; struct CBTree *cbtree; struct md5_ctx md5; struct Heap *heap; char buf[128]; static_assert(sizeof(int) >= 4, "unsupported int size"); heap = heap_create(heap_is_better, NULL, NULL); heap_top(heap); aatree_init(&aatree, NULL, NULL); cbtree = cbtree_create(NULL, NULL, NULL, NULL); cbtree_destroy(cbtree); daemonize(NULL, false); hash_lookup3("foo", 3); if (!event_init()) log_debug("test"); if (!parse_ini_file("foo", NULL, NULL)) log_debug("test"); log_stats("1"); file_size("foo"); md5_reset(&md5); strlcpy(buf, "foo", sizeof(buf)); printf("xmalloc: %p\n", xmalloc(128)); if (0) die("0"); csrandom(); tls_init(); return 0; } pgqd/lib/test/test_time.c0000664000401600040160000000175213175113172013747 0ustar cbecbe #include #include #include "test_common.h" static void test_get_time(void *p) { usec_t t, t2; usec_t ct, ct2; t = get_time_usec(); ct = get_cached_time(); usleep(USEC / 4); t2 = get_time_usec(); tt_assert(t + USEC / 4 <= t2); ct2 = get_cached_time(); tt_assert(ct2 == ct); reset_time_cache(); ct2 = get_cached_time(); tt_assert(ct2 != ct); end:; } static void test_time_format(void *p) { char buf[128]; usec_t t; #ifdef WIN32 tt_assert(_putenv("TZ=GMT") >= 0); _tzset(); printf( "_daylight = %d\n", _daylight ); printf( "_timezone = %ld\n", _timezone ); printf( "_tzname[0] = %s\n", _tzname[0] ); #else setenv("TZ", "GMT", 1); tzset(); #endif t = 1226059006841546; str_check(format_time_ms(t, buf, sizeof(buf)), "2008-11-07 11:56:46.841"); str_check(format_time_s(t, buf, sizeof(buf)), "2008-11-07 11:56:46"); end:; } struct testcase_t time_tests[] = { { "gettime", test_get_time }, { "format", test_time_format }, END_OF_TESTCASES }; pgqd/lib/test/test_string.c0000664000401600040160000003715013175113172014320 0ustar cbecbe #include #include #ifdef HAVE_LIBGEN_H #include #endif #undef basename #undef dirname #include #include #include "test_common.h" /* * strlcpy */ static char *run_strlcpy(char *dst, const char *src, int size, int expres) { int res; memcpy(dst, "XXX", 4); res = strlcpy(dst, src, size); if (res != expres) return "FAIL"; return dst; } static void test_strlcpy(void *ptr) { char buf[128]; str_check(run_strlcpy(buf, "", 16, 0), ""); str_check(run_strlcpy(buf, "", 0, 0), "XXX"); str_check(run_strlcpy(buf, "", 16, 0), ""); str_check(run_strlcpy(buf, "abc", 16, 3), "abc"); str_check(run_strlcpy(buf, "abc", 4, 3), "abc"); str_check(run_strlcpy(buf, "abc", 3, 3), "ab"); str_check(run_strlcpy(buf, "abc", 2, 3), "a"); str_check(run_strlcpy(buf, "abc", 1, 3), ""); str_check(run_strlcpy(buf, "abc", 0, 3), "XXX"); end:; } /* * strlcat */ static char *run_strlcat(char *dst, const char *src, int size, int expres) { int res; memcpy(dst, "PFX", 4); res = strlcat(dst, src, size); if (res != expres) return "FAIL"; return dst; } static void test_strlcat(void *ptr) { char buf[128]; str_check(run_strlcat(buf, "", 16, 3), "PFX"); str_check(run_strlcat(buf, "abc", 16, 6), "PFXabc"); str_check(run_strlcat(buf, "abc", 7, 6), "PFXabc"); str_check(run_strlcat(buf, "abc", 6, 6), "PFXab"); str_check(run_strlcat(buf, "abc", 5, 6), "PFXa"); str_check(run_strlcat(buf, "abc", 4, 6), "PFX"); str_check(run_strlcat(buf, "abc", 3, 6), "PFX"); str_check(run_strlcat(buf, "abc", 2, 5), "PFX"); str_check(run_strlcat(buf, "abc", 1, 4), "PFX"); str_check(run_strlcat(buf, "abc", 0, 3), "PFX"); end:; } /* * strnlen() */ static void test_strnlen(void *p) { tt_assert(strnlen("foobar", 0) == 0); tt_assert(strnlen("foobar", 1) == 1); tt_assert(strnlen("foobar", 2) == 2); tt_assert(strnlen("foobar", 3) == 3); tt_assert(strnlen("foobar", 4) == 4); tt_assert(strnlen("foobar", 5) == 5); tt_assert(strnlen("foobar", 6) == 6); tt_assert(strnlen("foobar", 7) == 6); tt_assert(strnlen("foobar", 8) == 6); tt_assert(strnlen("foobar", 9) == 6); end:; } /* * strerror_r() */ static void test_strerror_r(void *p) { char buf[128]; /* "int" vs. "const char *" */ tt_assert(strerror_r(EINTR, buf, sizeof(buf)) != 0); tt_assert(strlen(strerror_r(EINTR, buf, sizeof(buf))) != 0); end:; } /* * memrchr */ static void test_memrchr(void *p) { static const char data[] = "abcdabc"; tt_assert(memrchr(data, 'a', 8) == data + 4); tt_assert(memrchr(data, 'a', 4) == data + 0); tt_assert(memrchr(data, 'd', 8) == data + 3); tt_assert(memrchr(data, 'x', 8) == NULL); end:; } /* * memmem */ static int zmemm(const char *s, const char *q) { char *r = memmem(s, strlen(s), q, strlen(q)); return r ? (r - s) : -1; } static void test_memmem(void *p) { int_check(zmemm("qwe", ""), 0); int_check(zmemm("qwe", "q"), 0); int_check(zmemm("qwe", "w"), 1); int_check(zmemm("qwe", "e"), 2); int_check(zmemm("qwe", "x"), -1); int_check(zmemm("qwe", "qw"), 0); int_check(zmemm("qwe", "we"), 1); int_check(zmemm("qwe", "qx"), -1); int_check(zmemm("qwe", "wx"), -1); int_check(zmemm("qwe", "ex"), -1); int_check(zmemm("qwe", "qwe"), 0); int_check(zmemm("qwe", "qwx"), -1); int_check(zmemm("qwe", "qxe"), -1); int_check(zmemm("qwe", "xwe"), -1); int_check(zmemm("qweqweza", "qweza"), 3); int_check(zmemm("qweqweza", "weza"), 4); int_check(zmemm("qweqweza", "eza"), 5); int_check(zmemm("qweqweza", "za"), 6); int_check(zmemm("qweqweza", "a"), 7); int_check(zmemm("qweqweza", "qwez"), 3); int_check(zmemm("qweqweza", "wez"), 4); int_check(zmemm("qweqweza", "ez"), 5); int_check(zmemm("qweqweza", "z"), 6); int_check(zmemm("qweqwez", "qweza"), -1); int_check(zmemm("qweqwez", "weza"), -1); int_check(zmemm("qweqwez", "eza"), -1); int_check(zmemm("qweqwez", "za"), -1); int_check(zmemm("qweqwez", "a"), -1); end:; } /* * mempcpy */ static void test_mempcpy(void *p) { char buf[128]; memset(buf, 0, sizeof buf); tt_assert(mempcpy(buf, "xx", 0) == buf); str_check(buf, ""); tt_assert(mempcpy(buf, "xx", 1) == buf+1); str_check(buf, "x"); tt_assert(mempcpy(buf, "yy", 2) == buf+2); str_check(buf, "yy"); end:; } /* * strpcpy */ static int run_strpcpy(char *dst, const char *src, int size) { char *res; memcpy(dst, "XXX", 4); res = strpcpy(dst, src, size); if (res == NULL) { if (size == 0) { if (strcmp(dst, "XXX") != 0) return -10; } else { if (memcmp(dst, src, size - 1) != 0) return -11; if (dst[size-1] != '\0') return -12; } return -1; } if (*res != '\0') return -13; if (memcmp(dst, src, res-dst) != 0) return -14; if (res < dst) return -15; return res - dst; } static void test_strpcpy(void *p) { char buf[128]; memset(buf, 0, sizeof buf); int_check(run_strpcpy(buf, "", 0), -1); int_check(run_strpcpy(buf, "", 1), 0); int_check(run_strpcpy(buf, "a", 0), -1); int_check(run_strpcpy(buf, "a", 1), -1); int_check(run_strpcpy(buf, "a", 2), 1); int_check(run_strpcpy(buf, "asd", 1), -1); int_check(run_strpcpy(buf, "asd", 2), -1); int_check(run_strpcpy(buf, "asd", 3), -1); int_check(run_strpcpy(buf, "asd", 4), 3); int_check(run_strpcpy(buf, "asd", 5), 3); end:; } /* * strpcat */ static int run_strpcat(char *dst, const char *src, int size) { char *res; char copydst[1024]; char copy[1024]; strlcpy(copydst, dst, sizeof copy); strlcpy(copy, dst, sizeof copy); strlcat(copy, src, sizeof copy); res = strpcat(dst, src, size); if (res == NULL) { if (size == 0) { if (strcmp(dst, copydst) != 0) return -10; } else { if (memcmp(dst, copy, size - 1) != 0) return -11; if (dst[size-1] != '\0') return -12; } return -1; } if (*res != '\0') return -13; if (memcmp(dst, copy, res-dst) != 0) return -14; if (res < dst) return -15; return res - dst; } static void test_strpcat(void *p) { char buf[128]; memset(buf, 0, sizeof buf); int_check(run_strpcat(buf, "", 0), -1); int_check(run_strpcat(buf, "", 1), 0); int_check(run_strpcat(buf, "a", 1), -1); int_check(run_strpcat(buf, "a", 2), 1); str_check(buf, "a"); int_check(run_strpcat(buf, "b", 0), -1); int_check(run_strpcat(buf, "b", 1), -12); int_check(run_strpcat(buf, "b", 2), -1); int_check(run_strpcat(buf, "b", 3), 2); str_check(buf, "ab"); end:; } /* * basename */ static const char *run_basename(const char *path) { static char buf[128]; const char *res; if (!path) return basename(NULL); strlcpy(buf, path, sizeof(buf)); res = basename(buf); if (strcmp(buf, path) != 0) return "MODIFIES"; return res; } static void test_basename(void *p) { str_check(run_basename("/usr/lib"), "lib"); str_check(run_basename("/usr/"), "usr"); str_check(run_basename("/"), "/"); str_check(run_basename("///"), "/"); str_check(run_basename("//usr//lib//"), "lib"); str_check(run_basename(""), "."); str_check(run_basename("a/"), "a"); str_check(run_basename(NULL), "."); end:; } /* * dirname */ static const char *run_dirname(const char *path) { static char buf[128]; const char *res; if (!path) return dirname(NULL); strlcpy(buf, path, sizeof(buf)); res = dirname(buf); if (strcmp(buf, path) != 0) return "MODIFIES"; return res; } static void test_dirname(void *p) { str_check(run_dirname("/usr/lib"), "/usr"); str_check(run_dirname("/usr/"), "/"); str_check(run_dirname("usr"), "."); str_check(run_dirname("/usr/"), "/"); str_check(run_dirname("/"), "/"); str_check(run_dirname("/"), "/"); str_check(run_dirname(".."), "."); str_check(run_dirname("."), "."); str_check(run_dirname(""), "."); str_check(run_dirname("a/"), "."); str_check(run_dirname("a//"), "."); str_check(run_dirname(NULL), "."); end:; } /* * strlist */ static bool slshow(void *arg, const char *s) { struct MBuf *mb = arg; if (mbuf_written(mb) > 0) { if (!mbuf_write_byte(mb, ',')) return false; } if (!s) s = "NULL"; return mbuf_write(mb, s, strlen(s)); } static const char *lshow(const struct StrList *sl) { static char buf[128]; bool ok; struct MBuf mb; mbuf_init_fixed_writer(&mb, buf, sizeof(buf)); ok = strlist_foreach(sl, slshow, &mb); if (!ok) return "FAIL"; ok = mbuf_write_byte(&mb, 0); if (!ok) return "FAIL"; return buf; } static void test_strlist(void *p) { struct StrList *sl = NULL; const char *s; sl = strlist_new(NULL); str_check(lshow(sl), ""); strlist_append(sl, "1"); str_check(lshow(sl), "1"); strlist_append(sl, "2"); str_check(lshow(sl), "1,2"); strlist_append(sl, "3"); str_check(lshow(sl), "1,2,3"); s = strlist_pop(sl); str_check(s, "1"); free(s); strlist_append(sl, NULL); str_check(lshow(sl), "2,3,NULL"); strlist_free(sl); end:; } static bool sl_add(void *arg, const char *s) { return strlist_append(arg, s); } static const char *wlist(const char *s) { const char *res = "FAIL"; struct StrList *sl = strlist_new(NULL); bool ok = parse_word_list(s, sl_add, sl); if (ok) { if (strlist_empty(sl)) res = "-"; else res = lshow(sl); } strlist_free(sl); return res; } static void test_wlist(void *p) { str_check(wlist("1,2,3"), "1,2,3"); str_check(wlist(" 1 , \n 2 \t , \t3"), "1,2,3"); str_check(wlist(" 1 "), "1"); str_check(wlist(" 1 ,"), "1"); str_check(wlist(", 1 "), "1"); str_check(wlist("1 2"), "1 2"); str_check(wlist(" "), ""); end:; } static void test_mempbrk(void *z) { const char *p = "0123456789"; tt_assert(mempbrk(p, 10, "", 0) == NULL); tt_assert(mempbrk(p, 10, "a", 0) == NULL); tt_assert(mempbrk(p, 10, "ab", 0) == NULL); tt_assert(mempbrk(p, 10, "abc", 0) == NULL); tt_assert(mempbrk(p, 10, "1", 1) == p+1); tt_assert(mempbrk(p, 10, "12", 2) == p+1); tt_assert(mempbrk(p, 10, "21", 2) == p+1); tt_assert(mempbrk(p, 10, "123", 3) == p+1); tt_assert(mempbrk(p, 10, "321", 3) == p+1); tt_assert(mempbrk(p, 11, "abc\0", 4) == p+10); end:; } static void test_memcspn(void *z) { int_check(memcspn("qwe", 3, "", 0), 3); int_check(memcspn("qwe", 3, "w", 1), 1); int_check(memcspn("qwe", 3, "z", 1), 3); int_check(memcspn("qwe", 3, "we", 2), 1); int_check(memcspn("qwe", 3, "eq", 2), 0); int_check(memcspn("qwe", 3, "zx", 2), 3); int_check(memcspn("qwe", 3, "wez", 3), 1); int_check(memcspn("qwe", 3, "ewz", 3), 1); int_check(memcspn("qwe", 3, "zxa", 3), 3); int_check(memcspn("qwe", 3, "weza", 4), 1); int_check(memcspn("qwe", 3, "azew", 4), 1); int_check(memcspn("qwe", 3, "zxab", 4), 3); end:; } static void test_memspn(void *z) { const char *d = "0123456789"; int_check(memspn(d, 10, "", 0), 0); int_check(memspn(d, 10, "0", 1), 1); int_check(memspn(d, 10, "1", 1), 0); int_check(memspn(d, 10, "23", 2), 0); int_check(memspn(d, 10, "01", 2), 2); int_check(memspn(d, 10, "456", 3), 0); int_check(memspn(d, 10, "012", 3), 3); int_check(memspn(d, 10, "4567", 4), 0); int_check(memspn(d, 10, "0123", 4), 4); int_check(memspn(d, 10, d, 10), 10); int_check(memspn(d, 11, d, 11), 11); end:; } static void test_s2d_dot(void *p) { char buf[128]; double val; char *end; memset(buf, 0, sizeof(buf)); dtostr_dot(buf, sizeof(buf), 1.5); str_check(buf, "1.5"); val = strtod_dot(buf, &end); tt_assert(val == 1.5); tt_assert(*end == 0); end:; } static const char *wrap_strtonum(const char *s, long long minval, long long maxval) { static char buf[256]; long long res, res1; const char *err = "HUH"; errno = EPERM; res1 = strtonum(s, minval, maxval, NULL); errno = EPERM; res = strtonum(s, minval, maxval, &err); if (err && (res != 0 || !errno || errno == ENOMEM)) return "ERRBUG"; if (!err && errno != EPERM) return "ERRBACKUP"; if (res1 != res) return "EH"; if (!err) { snprintf(buf, sizeof buf, "%lld", res); return buf; } snprintf(buf, sizeof buf, "E:%s", err); return buf; } static void test_strtonum(void *p) { str_check(wrap_strtonum("1", -10, 50), "1"); str_check(wrap_strtonum("-11", -100, -1), "-11"); str_check(wrap_strtonum("", 1, 9), "E:invalid"); str_check(wrap_strtonum(" ", 1, 9), "E:invalid"); str_check(wrap_strtonum(" x", 1, 9), "E:invalid"); str_check(wrap_strtonum(" 5.5", 1, 9), "E:invalid"); str_check(wrap_strtonum(" 5 ", 1, 9), "E:invalid"); str_check(wrap_strtonum("0x05", 1, 9), "E:invalid"); str_check(wrap_strtonum("0", 1, 9), "E:too small"); str_check(wrap_strtonum("11", 1, 9), "E:too large"); str_check(wrap_strtonum(" 5", 1, 9), "5"); str_check(wrap_strtonum(" -5", -10, 10), "-5"); str_check(wrap_strtonum("5", -5, 5), "5"); str_check(wrap_strtonum("-5", -5, 5), "-5"); str_check(wrap_strtonum("6", -5, 5), "E:too large"); str_check(wrap_strtonum("-6", -5, 5), "E:too small"); /* limits */ str_check(wrap_strtonum(" 9223372036854775807", LLONG_MIN, LLONG_MAX), "9223372036854775807"); str_check(wrap_strtonum(" 9223372036854775808", LLONG_MIN, LLONG_MAX), "E:too large"); str_check(wrap_strtonum(" 9900000000000000000", LLONG_MIN, LLONG_MAX), "E:too large"); str_check(wrap_strtonum("-9223372036854775808", LLONG_MIN, LLONG_MAX), "-9223372036854775808"); str_check(wrap_strtonum("-9223372036854775809", LLONG_MIN, LLONG_MAX), "E:too small"); str_check(wrap_strtonum("-9900000000000000000", LLONG_MIN, LLONG_MAX), "E:too small"); str_check(wrap_strtonum("-10", LLONG_MIN, 0), "-10"); str_check(wrap_strtonum("10", 0, LLONG_MAX), "10"); str_check(wrap_strtonum(" 9223372036854775807", -10, 10), "E:too large"); str_check(wrap_strtonum("-9223372036854775808", -10, 10), "E:too small"); end:; } static const char *run_strsep(const char *input, const char *delim) { static char buf[1024]; static char res[1024]; char *ptr, *tok; strlcpy(buf, input, sizeof(buf)); res[0] = 0; for (ptr = buf; ptr; ) { tok = strsep(&ptr, delim); if (!tok) tok = "NULL"; strlcat(res, "(", sizeof(res)); strlcat(res, tok, sizeof(res)); strlcat(res, ")", sizeof(res)); } return res; } static void test_strsep(void *p) { char *ptr = NULL; tt_assert(strsep(&ptr, "x") == NULL); str_check(run_strsep("", ""), "()"); str_check(run_strsep("qwe", ""), "(qwe)"); str_check(run_strsep("", ","), "()"); str_check(run_strsep("a,b,,c", ","), "(a)(b)()(c)"); str_check(run_strsep(",,", ","), "()()()"); str_check(run_strsep(",:,", ",:"), "()()()()"); str_check(run_strsep(",:,", ":,"), "()()()()"); str_check(run_strsep("", ",:"), "()"); str_check(run_strsep(" a , b : c ", ",:"), "( a )( b )( c )"); end:; } static void test_snprintf(void *p) { char buf[32]; char *longstr = "0123456789"; int_check(snprintf(buf, 7, "%s", longstr), 10); int_check(snprintf(buf, 8, "%s", longstr), 10); int_check(snprintf(buf, 9, "%s", longstr), 10); int_check(snprintf(buf, 10, "%s", longstr), 10); int_check(snprintf(buf, 11, "%s", longstr), 10); int_check(snprintf(buf, 12, "%s", longstr), 10); end:; } static void test_asprintf(void *p) { char *res = NULL; int_check(asprintf(&res, "%s", "1234"), 4); str_check(res, "1234"); free(res); end:; } _PRINTF(2,3) static int tmp_asprintf(char **dst, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = vasprintf(dst, fmt, ap); va_end(ap); return res; } static void test_vasprintf(void *p) { char *res = NULL; int_check(tmp_asprintf(&res, "%s", "1234"), 4); str_check(res, "1234"); free(res); end:; } /* * Describe */ struct testcase_t string_tests[] = { { "strlcpy", test_strlcpy }, { "strlcat", test_strlcat }, { "strnlen", test_strnlen }, { "strerror_r", test_strerror_r }, { "memrchr", test_memrchr }, { "memmem", test_memmem }, { "mempbrk", test_mempbrk }, { "memcspn", test_memcspn }, { "memspn", test_memspn}, { "mempcpy", test_mempcpy }, { "strsep", test_strsep }, { "strpcpy", test_strpcpy }, { "strpcat", test_strpcat }, { "basename", test_basename }, { "dirname", test_dirname }, { "strlist", test_strlist }, { "parse_wordlist", test_wlist }, { "str2double_dot", test_s2d_dot }, { "strtonum", test_strtonum }, { "snprintf", test_snprintf }, { "asprintf", test_asprintf }, { "vasprintf", test_vasprintf }, END_OF_TESTCASES }; pgqd/lib/test/coverage.sh0000775000401600040160000000116513175113172013736 0ustar cbecbe#! /bin/sh set -e test -f test_common.h || { echo "wrong dir" exit 1 } cd .. rm -rf .objs test/.objs rm -rf test/lcov/* make -C test test_config.h make -f test/coverage.mk CC="gcc -fprofile-arcs -ftest-coverage" CFLAGS="-O0 -g" ./covtest || true echo 'Running lcov' lcov -q --capture --directory .objs/covtest -b . --output-file coverage.info.tmp echo 'Fixing filenames' sed -e '/SF:/s,/\./,/,' coverage.info.tmp > coverage.info echo 'Running genhtml' genhtml -q coverage.info --output-directory test/lcov echo 'Result: test/lcov/index.html' rm -f coverage.info.tmp coverage.info rm -f covtest rm -rf .objs/covtest pgqd/lib/test/test_talloc.c0000664000401600040160000002612313175113172014266 0ustar cbecbe#include "test_common.h" #include #include #include #include #undef TALLOC_POS #define TALLOC_POS(x) x static int delta; static int dcount; static int ptr_nr_counter; #define CK_MAGIC 0x1EEF static char log_buf[1024]; static LIST(trace_alloc_list); struct CheckHeader { short magic; short ptr_nr; int size; struct List trace_node; }; static void m_log(char code, struct CheckHeader *hdr) { char buf[32]; const char *sep = log_buf[0] ? ", " : ""; size_t res; tt_assert(hdr->magic == CK_MAGIC); snprintf(buf, sizeof(buf), "%s%c:%u", sep, code, hdr->ptr_nr); res = strlcat(log_buf, buf, sizeof(log_buf)); tt_assert(res < sizeof(log_buf)); end:; } static void *log_alloc(void *ctx, size_t len) { struct CheckHeader *chdr; delta += len; chdr = cx_alloc(ctx, len + sizeof(*chdr)); if (!chdr) return NULL; memset(chdr, 0x89, len + sizeof(*chdr)); chdr->magic = CK_MAGIC; chdr->ptr_nr = ++ptr_nr_counter; chdr->size = len; list_init(&chdr->trace_node); list_append(&trace_alloc_list, &chdr->trace_node); m_log('A', chdr); return chdr + 1; } static void *log_realloc(void *ctx, void *ptr, size_t len) { int olen; struct CheckHeader *chdr = ptr; chdr--; m_log('R', chdr); olen = chdr->size; list_del(&chdr->trace_node); chdr = cx_realloc(ctx, chdr, len + sizeof(*chdr)); list_append(&trace_alloc_list, &chdr->trace_node); chdr->size = len; delta -= olen; delta += len; return chdr + 1; } static void log_free(void *ctx, const void *ptr) { struct CheckHeader *chdr = (void*)ptr; chdr--; m_log('F', chdr); delta -= chdr->size; list_del(&chdr->trace_node); memset((void*)ptr, 0x95, chdr->size); chdr->size = chdr->magic = 0; cx_free(ctx, chdr); } static const struct CxOps log_ops = { log_alloc, log_realloc, log_free, }; static const struct CxMem log_libc = { &log_ops, (void*)&cx_libc_allocator, }; static void log_reset(void) { struct CheckHeader *chdr; struct List *el; while (!list_empty(&trace_alloc_list)) { //printf("memleak\n"); el = list_first(&trace_alloc_list); chdr = container_of(el, struct CheckHeader, trace_node); list_del(&chdr->trace_node); memset(chdr, 0x95, chdr->size); cx_free(NULL, chdr); } log_buf[0] = 0; delta = 0; ptr_nr_counter = 0; } #define log_check(x) do { str_check(log_buf, x); log_buf[0] = 0; } while (0) #define log_check_full(x) do { str_check(log_buf, x); int_check(delta, 0); log_reset(); } while (0) #define log_check_quick() do { int_check(delta, 0); log_reset(); } while (0) static void *create_top(void) { log_reset(); return talloc_from_cx(&log_libc, 10, "top"); } static int destructor1(void *ptr) { dcount++; return 0; } static int destructor2(void *ptr) { dcount++; talloc_free(ptr); return 0; } struct DumpState { struct MBuf *dst; int prev_depth; bool failed; }; static void dump_cb(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg) { struct DumpState *st = cb_arg; const char *name; if (depth == st->prev_depth) { if (!mbuf_write_byte(st->dst, ',')) goto failed; } while (st->prev_depth < depth) { if (!mbuf_write_byte(st->dst, '[')) goto failed; st->prev_depth++; } while (st->prev_depth > depth) { if (!mbuf_write_byte(st->dst, ']')) goto failed; st->prev_depth--; } if (is_ref) { if (!mbuf_write_byte(st->dst, '>')) goto failed; } name = talloc_get_name(ptr); if (!mbuf_write(st->dst, name, strlen(name))) goto failed; return; failed: st->failed = true; } static const char *dump_talloc(void *ptr) { struct DumpState state; struct MBuf dst; static char buf[1024]; mbuf_init_fixed_writer(&dst, buf, sizeof(buf)); state.dst = &dst; state.prev_depth = 0; talloc_report_depth_cb(ptr, 1, 100, dump_cb, &state); while (state.prev_depth > 0) { if (!mbuf_write_byte(state.dst, ']')) return "failed"; state.prev_depth--; } if (!mbuf_write_byte(state.dst, 0)) return "failed"; return buf; } static void test_talloc_basic(void *zzz) { void *p, *p2, *p3; void *top; const char *name1 = "name1"; struct CheckHeader *chp; /* basic */ top = create_top(); tt_assert(top != NULL); p2 = talloc_size(top, 16); tt_assert(p2 != NULL); p = talloc_named_const(top, 16, "p"); tt_assert(p != NULL); p3 = talloc_size(top, 16); tt_assert(p3 != NULL); p = talloc_realloc_fn(p, p, 500); //talloc_set_name_const(p, "p-realloc"); str_check(dump_talloc(top), "[top[talloc_size,talloc_realloc_fn,talloc_size]]"); tt_assert(p); talloc_free(top); log_check_quick(); /* name */ top = create_top(); tt_assert(top != NULL); str_check(talloc_get_name(top), "top"); talloc_set_name_const(top, name1); tt_assert(talloc_get_name(top) == name1); talloc_set_name(top, "foo: %d", 10); str_check(talloc_get_name(top), "foo: 10"); tt_assert(talloc_check_name(top, "xx") == NULL); tt_assert(talloc_check_name(top, "foo: 10") == top); talloc_free(top); log_check_full("A:1, A:2, F:2, F:1"); top = create_top(); p = talloc(top, struct CheckHeader); str_check(dump_talloc(top), "[top[struct CheckHeader]]"); talloc_free(top); log_check_full("A:1, A:2, F:2, F:1"); /* init & NULL ctx */ top = talloc_init("test std init: %d", 1); tt_assert(talloc_check_name(top, "test std init: 1")); p = talloc(top, struct CheckHeader); tt_assert(p != NULL); tt_assert(talloc_get_size(p) == sizeof(struct CheckHeader)); tt_assert(talloc_check_name(p, "struct CheckHeader")); talloc_free(top); /* other inits */ top = create_top(); tt_assert(top != NULL); p = talloc_named_const(top, 10, name1); tt_assert(talloc_check_name(p, name1)); p2 = talloc_named(p, 10, "test: %d", 1); tt_assert(talloc_check_name(p2, "test: 1")); p3 = talloc_zero_named_const(p2, 1024, "zero"); tt_assert(talloc_check_name(p3, "zero")); tt_assert(talloc_parent(p3) == p2); tt_assert(talloc_parent_name(p2) == name1); tt_assert(talloc_is_parent(p2, p3)); tt_assert(talloc_is_parent(top, p2)); talloc_free(top); log_check_quick(); /* destructor */ top = create_top(); p = talloc_named_const(top, 10, name1); p2 = talloc_named_const(top, 10, name1); p3 = talloc_named_const(top, 10, name1); tt_assert(top && p && p2 && p3); talloc_set_destructor(p, NULL); talloc_set_destructor(p2, destructor1); talloc_set_destructor(p3, destructor2); talloc_free(p3); int_check(dcount, 1); talloc_free(top); int_check(delta, 0); int_check(dcount, 2); /* types */ top = create_top(); chp = talloc_ptrtype(top, chp); tt_assert(chp); talloc_free(top); int_check(delta, 0); end:; } static void test_talloc_strings(void *zzz) { char *a, *b, *c; const char *x = "foo\0bar"; void *top; log_reset(); top = create_top(); a = talloc_memdup(top, x, 8); tt_assert(a && talloc_get_size(a) == 8); tt_assert(memcmp(a, x, 8) == 0); b = talloc_strdup(top, "baz"); tt_assert(b && talloc_get_size(b) == 4); a = talloc_strdup_append(a, "zzz"); str_check(a, "foozzz"); c = talloc_memdup(top, x, 8); c = talloc_strdup_append_buffer(c, "zzz"); int_check(talloc_get_size(c), 11); tt_assert(memcmp(c, "foo\0barzzz", 11) == 0); a = talloc_strndup(top, "qwe", 2); int_check(talloc_get_size(a), 3); str_check(a, "qw"); talloc_free(top); log_check_quick(); end:; } static int destruct_calls; static int test_destructor(void *ptr) { destruct_calls++; return 0; } static void test_talloc_refs(void *zzz) { CxMem *cx = &log_libc; void *top, *top2; void *p1, *p2, *p3, *ref; int err; talloc_enable_null_tracking(); /* simple ref, freed from new parent */ top = talloc_from_cx(cx, 0, "top"); tt_assert(top != NULL); p1 = talloc_strdup(top, "p1"); tt_assert(p1); p2 = talloc_strdup(top, "p2"); tt_assert(p2); ref = talloc_reference(p2, p1); tt_assert(ref == p1); str_check(dump_talloc(top), "[top[p1,p2[>p1]]]"); err = talloc_free(p2); tt_assert(err == 0); str_check(dump_talloc(top), "[top[p1]]"); err = talloc_free(top); tt_assert(err == 0); log_check_full("A:1, A:2, A:3, A:4, F:4, F:3, F:2, F:1"); /* simple ref, free old parent */ top = talloc_from_cx(cx, 0, "top"); tt_assert(top != NULL); p1 = talloc_strdup(top, "p1"); tt_assert(p1); p2 = talloc_strdup(top, "p2"); tt_assert(p2); ref = talloc_reference(p2, p1); tt_assert(ref == p1); str_check(dump_talloc(top), "[top[p1,p2[>p1]]]"); err = talloc_free(p1); tt_assert(err == -1); err = talloc_unlink(top, p1); tt_assert(err == 0); str_check(dump_talloc(top), "[top[p2[p1]]]"); err = talloc_free(top); tt_assert(err == 0); log_check_full("A:1, A:2, A:3, A:4, F:4, F:2, F:3, F:1"); /* ref loop */ top = talloc_from_cx(cx, 0, "top"); tt_assert(top != NULL); top2 = talloc_strdup(top, "top2"); p1 = talloc_strdup(top2, "p1"); tt_assert(p1); p2 = talloc_strdup(p1, "p2"); tt_assert(p2); p3 = talloc_strdup(p1, "p3"); tt_assert(p2); talloc_set_destructor(top2, test_destructor); talloc_set_destructor(p2, test_destructor); ref = talloc_reference(p3, p1); tt_assert(ref == p1); str_check(dump_talloc(top), "[top[top2[p1[p2,p3[>p1]]]]]"); /* fail */ //printf("\n"); //talloc_set_debug(1); talloc_free(top2); talloc_set_debug(0); str_check(dump_talloc(top), "[top]"); //str_check(dump_talloc(NULL), "[]"); talloc_free(top); //str_check(dump_talloc(NULL), "[UNNAMED[p1[p2,p3[>p1]]]]"); //int_check(destruct_calls, 2); //log_check_full("A:1, A:2, A:3, A:4, A:5, A:6, F:6, F:2, F:1"); log_reset(); talloc_disable_null_tracking(); end:; } static void test_talloc_memlimit(void *pppp) { void *top, *l1, *l2, *l3, *tmp; int err; /* create memlimit ptr */ top = talloc_from_cx(&log_libc, 0, "top"); tt_assert(top); l1 = talloc_strdup(top, "l1"); tt_assert(l1); err = talloc_set_memlimit(l1, 1000); tt_assert(err == 0); /* too large */ tmp = talloc_size(l1, 1000); tt_assert(tmp == NULL); str_check(dump_talloc(top), "[top[l1[.memlimit]]]"); /* ok */ l2 = talloc_named_const(l1, 500, "l2"); tt_assert(l2); /* second level, too large */ tmp = talloc_size(l2, 500); tt_assert(tmp == NULL); str_check(dump_talloc(top), "[top[l1[.memlimit,l2]]]"); /* steal into memlimit */ l3 = talloc_named_const(top, 500, "l3"); tmp = talloc_steal(l2, l3); tt_assert(tmp == l3); str_check(dump_talloc(top), "[top[l1[.memlimit,l2[l3]]]]"); tmp = talloc_size(l2, 10); tt_assert(tmp == NULL); /* steal away from memlimit */ tmp = talloc_steal(top, l3); tt_assert(tmp == l3); str_check(dump_talloc(top), "[top[l1[.memlimit,l2]l3]]"); tmp = talloc_size(l2, 10); tt_assert(tmp); talloc_free(top); log_check_quick(); end:; } static void test_talloc_reparent(void *zzz) { CxMem *cx = &log_libc; void *top; void *p1, *p2, *p3, *ref; int err; log_reset(); top = talloc_from_cx(cx, 10, "top"); tt_assert(top); p1 = talloc_strdup(top, "p1"); tt_assert(p1); p2 = talloc_strdup(p1, "p2"); tt_assert(p2); p3 = talloc_strdup(p2, "p3"); tt_assert(p3); ref = talloc_reference(p1, p3); tt_assert(ref == p3); str_check(dump_talloc(top), "[top[p1[>p3,p2[p3]]]]"); err = talloc_free(p2); tt_assert(err == 0); str_check(dump_talloc(top), "[top[p1[p3]]]"); talloc_free(top); log_check_quick(); end:; } struct testcase_t talloc_tests[] = { { "basic", test_talloc_basic }, { "strings", test_talloc_strings }, { "refs", test_talloc_refs }, { "memlimit", test_talloc_memlimit }, { "reparent", test_talloc_reparent }, END_OF_TESTCASES }; pgqd/lib/test/test_wchar.c0000664000401600040160000000514513175113172014115 0ustar cbecbe #include #include #include "test_common.h" /* * mbstr_decode() */ static const char *decode(const char *s, int inbuf) { static char out[128]; wchar_t tmp[128]; wchar_t *res; int reslen = 4; unsigned i; for (i = 0; i < 128; i++) tmp[i] = '~'; res = mbstr_decode(s, inbuf, &reslen, tmp, sizeof(tmp), true); if (res == NULL) { if (errno == EILSEQ) return "EILSEQ"; if (errno == ENOMEM) return "ENOMEM"; return "NULL??"; } if (res != tmp) return "EBUF"; if (res[reslen] == 0) res[reslen] = 'Z'; else return "reslen fail?"; for (i = 0; i < 128; i++) { out[i] = tmp[i]; if (out[i] == '~') { out[i+1] = 0; break; } else if (out[i] == 0) { out[i] = '#'; } else if (tmp[i] > 127) { out[i] = 'A' + tmp[i] % 26; } } return out; } static void test_mbstr_decode(void *p) { str_check(decode("", 0), "Z~"); str_check(decode("", 1), "Z~"); str_check(decode("a", 0), "Z~"); str_check(decode("abc", 0), "Z~"); str_check(decode("abc", 1), "aZ~"); str_check(decode("abc", 2), "abZ~"); str_check(decode("abc", 3), "abcZ~"); str_check(decode("abc", 4), "abcZ~"); str_check(decode("abc", 5), "abcZ~"); if (MB_CUR_MAX > 1) { str_check(decode("aa\200cc", 5), "aaYccZ~"); str_check(decode("a\200cc", 5), "aYccZ~"); str_check(decode("aa\200c", 5), "aaYcZ~"); } end:; } /* * mbsnrtowcs() */ static const char *mbsnr(const char *str, int inbuf, int outbuf) { static char out[128]; wchar_t tmp[128]; int res; unsigned i; const char *s = str; mbstate_t ps; for (i = 0; i < 128; i++) tmp[i] = '~'; memset(&ps, 0, sizeof(ps)); res = mbsnrtowcs(tmp, &s, inbuf, outbuf, &ps); if (res < 0) { if (errno == EILSEQ) { snprintf(out, sizeof(out), "EILSEQ(%d)", (int)(s - str)); return out; } return "unknown error"; } if (tmp[res] == 0) tmp[res] = s ? 'z' : 'Z'; for (i = 0; i < 128; i++) { out[i] = tmp[i]; if (out[i] == '~') { out[i+1] = 0; break; } } return out; } static void test_mbsnrtowcs(void *p) { str_check(mbsnr("", 1, 1), "Z~"); str_check(mbsnr("", 0, 0), "~"); str_check(mbsnr("", 0, 1), "~"); /* XXX */ str_check(mbsnr("", 1, 0), "~"); str_check(mbsnr("x", 1, 1), "x~"); str_check(mbsnr("x", 0, 0), "~"); str_check(mbsnr("x", 0, 1), "~"); /* XXX */ str_check(mbsnr("x", 1, 0), "~"); str_check(mbsnr("abc", 3, 3), "abc~"); str_check(mbsnr("abc", 3, 4), "abc~"); /* XXX */ str_check(mbsnr("abc", 4, 3), "abc~"); str_check(mbsnr("abc", 4, 4), "abcZ~"); end:; } /* * Describe */ struct testcase_t wchar_tests[] = { { "mbsnrtowcs", test_mbsnrtowcs }, { "mbstr_decode", test_mbstr_decode }, END_OF_TESTCASES }; pgqd/lib/test/test_list.c0000664000401600040160000000574113175113172013766 0ustar cbecbe #include "test_common.h" #include #include #include struct MyNode { struct List node; int val; int seq; }; static int my_cmp(const struct List *a, const struct List *b) { struct MyNode *aa, *bb; aa = container_of(a, struct MyNode, node); bb = container_of(b, struct MyNode, node); if (aa->val < bb->val) return -1; if (aa->val > bb->val) return 1; return 0; } static bool check_list(struct List *list, int n) { struct List *old, *cur; int i; old = NULL; i = 0; for (cur = list->next; cur != list; cur = cur->next) { i++; if (old) { struct MyNode *mcur, *mold; mcur = container_of(cur, struct MyNode, node); mold = container_of(old, struct MyNode, node); if (mold->val > mcur->val) { printf("bad order(%d): old.val=%d new.val=%d", n, mold->val, mcur->val); return false; } if (mold->val == mcur->val && mold->seq > mcur->seq) { printf("unstable(%d): old.seq=%d new.seq=%d", n, mold->seq, mcur->seq); return false; } if (cur->prev != old) { printf("llist err 2 (n=%d)", n); return false; } } else { if (cur->prev != list) { printf("llist err (n=%d)", n); return false; } } old = cur; } if (list->prev != ((old) ? old : list)) { printf("llist err 3 (n=%d)", n); return false; } if (i != n) { printf("llist err 3 (n=%d)", n); return false; } return true; } static bool test_sort(void (*sort)(struct List *list, list_cmp_f cmp), int n) { struct MemPool *pool = NULL; struct List list[1]; bool ok; int i; /* random */ list_init(list); for (i = 0; i < n; i++) { struct MyNode *e = mempool_alloc(&pool, sizeof(*e)); list_init(&e->node); e->val = pseudo_random_range(100); e->seq = i; list_append(list, &e->node); } sort(list, my_cmp); ok = check_list(list, n); mempool_destroy(&pool); if (!ok) return false; /* seq */ list_init(list); for (i = 0; i < n; i++) { struct MyNode *e = mempool_alloc(&pool, sizeof(*e)); list_init(&e->node); e->val = i; e->seq = i; list_append(list, &e->node); } sort(list, my_cmp); ok = check_list(list, n); mempool_destroy(&pool); if (!ok) return false; /* reverse */ list_init(list); for (i = 0; i < n; i++) { struct MyNode *e = mempool_alloc(&pool, sizeof(*e)); list_init(&e->node); e->val = -i; e->seq = i; list_append(list, &e->node); } sort(list, my_cmp); ok = check_list(list, n); mempool_destroy(&pool); if (!ok) return false; return true; } static void test_list_sort(void *p) { int i; for (i = 0; i < 259; i++) tt_assert(test_sort(list_sort, i)); end:; } #if 0 static void test_list_sort2(void *p) { int i; for (i = 0; i < 259; i++) tt_assert(test_sort(list_sort2, i)); end:; } static void test_list_sort3(void *p) { int i; for (i = 0; i < 259; i++) tt_assert(test_sort(list_sort3, i)); end:; } #endif struct testcase_t list_tests[] = { { "sort", test_list_sort }, #if 0 { "sort2", test_list_sort2 }, { "sort3", test_list_sort3 }, #endif END_OF_TESTCASES }; pgqd/lib/test/ssl/0000775000401600040160000000000013175113172012402 5ustar cbecbepgqd/lib/test/ssl/ca2_server2.crt0000664000401600040160000000230413175113172015230 0ustar cbecbe-----BEGIN CERTIFICATE----- MIIDWTCCAkGgAwIBAgIVAK2Nx615+HaUPNo1T25Q27XRAL7WMA0GCSqGSIb3DQEB CwUAMBIxEDAOBgNVBAMMB1Rlc3RDQTIwIBcNMTAwMTAxMDgwNTAwWhgPMjA2MDEy MzEyMzU1MDBaMBYxFDASBgNVBAMMC3NlcnZlcjIuY29tMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAzO7Xgk/SDqkIUVG/G+7QZCQY++RkMMtE9co9df/5 Ke39frmHqg4ZBSRVq5/2bol/r9ykog8rjyhRtPj12PtLM1SSsTPW3+zmomrdx91U cAXBGt7yKJoZRJ/TJjZr6qI5PgGqa2gfOvRox6C9GLuL8D5nQoPXuRb6j1VpLA/Y nVwCV/PNsWSxa43t4+vAhtg+OWbLZiitWlB9fRgwmHv7EXISflvbuM3iuJMoPGF5 lv+dOiomad7epJyq4z1wsgFQ/GS0ZavHGdXpNUrRG5VnoWULa8d0kE1UOBErbRJR oHvX5Rw+AX+EbuX77H0pRO1woqNHUP9E0dbRDkKc6yvGMQIDAQABo4GfMIGcMAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB MB0GA1UdDgQWBBQVK3JgdO2re+e1LlLkaKpA8MVB6DAfBgNVHSMEGDAWgBQqZOrF iILkgQXcFW51FDA5KLWgnDAnBgNVHREEIDAeggtzZXJ2ZXIyLmNvbYIPd3d3LnNl cnZlcjIuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAJQXuJ/588Qx2AtE4Haj7emWAG 5pw0ewODN02GHPgf/PLTuk/CCroZEGXCzqgf2MK9UKQRLmoGDw3zESv+9s6yzavE tMyuss9VTxEP93YUhXcHcRpd8bWfXU4rPCaNTofi9r855SO8Hnk9Gc7/k1bmgYgV ybyqI9H9lvm7Pl4RXpwl1bsO/4/7jcDXpI8CXnQTZGC/IQA9O8tVDlVYzyl9STYb 6OZjOhfEhQHfcL2Z10ex6lq17jJTzTR1n3MnG3Dw8Sd1lH3JizxCnvY9cy6V1SrK p2sWn68yuOSw5AvkOtUrvUTT58ZJlY3mRh12k6KKa7tHqoMrNUayKjv+pY3m -----END CERTIFICATE----- pgqd/lib/test/ssl/ca1_client1.crt.sha2560000664000401600040160000000010013175113172016175 0ustar cbecbedd2e2a8492e110bf2ee63b534c10901eade00044e884a34851d914e22f3373c8pgqd/lib/test/ssl/ca2_server2.key0000664000401600040160000000321313175113172015230 0ustar cbecbe-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAzO7Xgk/SDqkIUVG/G+7QZCQY++RkMMtE9co9df/5Ke39frmH qg4ZBSRVq5/2bol/r9ykog8rjyhRtPj12PtLM1SSsTPW3+zmomrdx91UcAXBGt7y KJoZRJ/TJjZr6qI5PgGqa2gfOvRox6C9GLuL8D5nQoPXuRb6j1VpLA/YnVwCV/PN sWSxa43t4+vAhtg+OWbLZiitWlB9fRgwmHv7EXISflvbuM3iuJMoPGF5lv+dOiom ad7epJyq4z1wsgFQ/GS0ZavHGdXpNUrRG5VnoWULa8d0kE1UOBErbRJRoHvX5Rw+ AX+EbuX77H0pRO1woqNHUP9E0dbRDkKc6yvGMQIDAQABAoIBAAnsz1cqUnwPR63Y ja7mpAUVkngwmYGbdp2Iy+05FnInao2GosFjqWaMWfHT73VvB1YXE4odvfhS9ZIU NKPrl7rGr8wRPKjfqlPXZSJW384FGVMZ1RSOUgb2zp0dKaDVdnxATGajRk/uolx4 tm/KNX7PJuUbyYjXSg9DDbLqmY0VX3uGn005iq7qy28178J8v6/ULoF0s3QPlOVC UqCPmKu1Y+738jxyaD8ziZfGaZ2tpgkBADdl+VWMdMyJcSyqH4KIxJWryoqH1mZn L8j+04O+qScxnp6GXvQTKPmoJn9/jihaRQM4O8c31r1hj6llhJ6v/rf2OulofZLO yJ6ZSTkCgYEA7lbMWeyHTnaIEqPkmCVKQff4Q+5YP9k7y7jtT5l6p0Z907TlFijD wNAJEcM1AK5DoiAl/3g6ssKUP4hwV10EE12gc6PCD5cCvaTtuGP7qHiunfmTm1ZQ Tk0aYQdscSRL0JfN+0m6ssuYtc2NfRBPQF+7IWFIdY6x6DLc6mAAPSsCgYEA3B5X x1cbA78v7h9+cwihprbgYzPOGJfnSfnJIkFyd9I1SEZ9Sc5A7A/qphhjC18yFVe2 8nxaZLPqOendQNgxAeHZdKoCOAy3Bu1P2pkQRNbp+Xm4yJmQ02RCruiKmlfjbwg6 9FywacxOPsNy5Tt1iXMW0JtFqQ+Srtd/Ow/vtBMCgYEAy/Q29xSzTO+dzeW2LoRq oT0jlG4X0ruAsfrUCPPbw91Mu6fnCilkKlFcEXksmCKH+ers2Ur3YBWmax/Ekt4k KLZMIekWneSqEcNbMhlNN4PeWY1DRQB9xCOy2SlTIRbgUeY1/D3BqFMVoS3/T/Bv ATVqm0v2h7LOh1dPOLliOvUCgYAanGR6SfKPImb1/8K2v31jfUplU9b/rCWfEN6b EJR5HeWpJjRxXskEgOwaRmp67nhMkj0g1Z3L/OA9PiOsejZ6hsZWT1NTWV4rLTdx Mv3/hIfmzCtlvQMAO2fAY4aWIDfzrWgLMMV3FOcEuXZDdkvAYTSJ5aunQ6W1E4Jj LosJFwKBgBhuRFBEcASD5Z/lar7dQHXW2GPgprTS45Ye/z76fZFRKJEKitjBAL3n In2/HXnYN7cafiyMTIjHdHfcuh5AW+77uXTeANn2/ou+rKwuRhtO8r1T1fy2u/vX CM5qjZXX2d+1NOvYKXyMOU06yDPIkCta7A+DohWVoEqyR9XrqH8a -----END RSA PRIVATE KEY----- pgqd/lib/test/ssl/ca1_root.key0000664000401600040160000000044013175113172014621 0ustar cbecbe-----BEGIN EC PRIVATE KEY----- MIGkAgEBBDBlu3Ucyitk/BhZoVOcoztILRVeI0TJaHtpVI9WcW5RkxHrfXlEjPyC ZiauXt8xhKygBwYFK4EEACKhZANiAATJzWRHlzYeHQpGG/xAf8jAGfUdVObVuAlI ZhBXhw29EEQrGLKAdgGmQ7VdsFJD5SWF1sSIrKvqarpjWvGZCN5n+7DcL9DlBSgt eudIjQR7NjY4np4yHN5KrjQ8/EfQs2M= -----END EC PRIVATE KEY----- pgqd/lib/test/ssl/ca2_root.crt.sha10000664000401600040160000000005013175113172015452 0ustar cbecbe0a9bdb6f46264ac2476ab83d9b775b89ea39a561pgqd/lib/test/ssl/ca1_server1.key0000664000401600040160000000044013175113172015225 0ustar cbecbe-----BEGIN EC PRIVATE KEY----- MIGkAgEBBDCOr5tmsJD/wkOhSzL1GsnslFsbZOJ6UiKwtIdv852OB20gR5nMighZ 4tkfhljvilWgBwYFK4EEACKhZANiAASooSXtvLn0bU8qzbnngEa+DahIdzfjw4An B1AuR9zuFfXUnDGnvg+WGJhdmmSHfzwbWr6QaGLpYIi9LAqHPPfQAaKcH+s6OFYc 090rTuxSe8g+nnBpATRnWRUJvktFios= -----END EC PRIVATE KEY----- pgqd/lib/test/ssl/ca1_client1.crt0000664000401600040160000000140113175113172015173 0ustar cbecbe-----BEGIN CERTIFICATE----- MIICDDCCAZKgAwIBAgIVAI3TbYGMiPbSqobFjdswC9D4WRGXMAoGCCqGSM49BAMC ME8xEDAOBgNVBAMMB1Rlc3RDQTExCzAJBgNVBAYTAkFBMQ4wDAYDVQQHDAVDaXR5 MTEPMA0GA1UECAwGU3RhdGUxMQ0wCwYDVQQKDARPcmcxMCAXDTEwMDEwMTA4MDUw MFoYDzIwNjAxMjMxMjM1NTAwWjASMRAwDgYDVQQDDAdjbGllbnQxMEkwEwYHKoZI zj0CAQYIKoZIzj0DAQEDMgAEhqUAC1EUCE8FeSGQeGYHTy9XLDx0RVVUA7FSc61C r1UB9Fqh8Soy4TU4DaqP6loXo4GVMIGSMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/ BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB0GA1UdDgQWBBR6XsZZlKrOrjAJ IcJElNEOfUWmZjAfBgNVHSMEGDAWgBSv7XTLHyo72n/hC1cXtXRtJdcurDAdBgNV HREEFjAUgRJjbGllbnRAY29tcGFueS5jb20wCgYIKoZIzj0EAwIDaAAwZQIxANi+ ZLRDs2cRXZ7U0pJ42AT2IUkBV6I0J+wVjtjAIHTDXn2XsW1q7r/NM94VTyu98QIw O7T2EPHdsTFJMiXX8sixMIH9BFO3yH06FGtUpYO/a6WuVO57hHFa0m9RC6QQvdm2 -----END CERTIFICATE----- pgqd/lib/test/ssl/ca1_client1.crt.sha10000664000401600040160000000005013175113172016025 0ustar cbecbe963c12ce3c48432789d41753363ec4f21d243a6fpgqd/lib/test/ssl/ca2_server2.crt.sha10000664000401600040160000000005013175113172016057 0ustar cbecbecd6f38b976470dcd2876febc733d83520a0f8d27pgqd/lib/test/ssl/ca2_client2.crt0000664000401600040160000000212313175113172015177 0ustar cbecbe-----BEGIN CERTIFICATE----- MIIDBTCCAe2gAwIBAgIVAPMTs9IPfgpoGhgOPBgu2OqNHNBuMA0GCSqGSIb3DQEB CwUAMBIxEDAOBgNVBAMMB1Rlc3RDQTIwIBcNMTAwMTAxMDgwNTAwWhgPMjA2MDEy MzEyMzU1MDBaME8xEDAOBgNVBAMMB2NsaWVudDIxCzAJBgNVBAYTAlhYMQ4wDAYD VQQHDAVDaXR5MjEPMA0GA1UECAwGU3RhdGUyMQ0wCwYDVQQKDARPcmcyMIGfMA0G CSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4Q52aWwmZbva0hJZWvvHqrCx1eIumscc3 d+nYCVcIvKBWHSM3lqT5wO/RWK4zwQOhs9HF9sfHunLVNRAn5opd1y8PUtTf9Ym6 Nf4NF4B4nFDrri5Z9PyCtg9U7RdeGOKqp/l1lQ2oJwkaLW37KZoyfdwTIyWgWc3b U7ukSkjo6QIDAQABo4GWMIGTMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWg MBMGA1UdJQQMMAoGCCsGAQUFBwMCMB0GA1UdDgQWBBQrBqtMOQBPDUmkKAMbk45u Cp0vbjAfBgNVHSMEGDAWgBQqZOrFiILkgQXcFW51FDA5KLWgnDAeBgNVHREEFzAV gRNjbGllbnQyQGNvbXBhbnkuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA0Uzool6jy dQfOsB6wpY1UuukvjGiOFjvn1ELbI5ijfsCjGGHsd7lHgqhNZlW9LaFZoXgy5Xez 9c5aFZfVYJsxVzA5invViNFuquteDJGLLlICudLowhqJUHZ2JbU38Ow5xdBjLpom URE+EwMd5M36xTUQYb1taOP78PWePOhhJx6pNqHPWgDGgHAL06cT4zn25kCzeBH0 be6MP8L8didXQCXkk+b5gvhwRrNgQEiwwR5WuMq+H8sTaNat8EiBKWTLJsnB82gM HsUmD8NjY2AstZcS2VRlT8OLpPYKgspxtLrbmWgdtMMGXpvp37hXY4tA5UEyqSvP WDYLZcnofVBm -----END CERTIFICATE----- pgqd/lib/test/ssl/ca1_root.crt.sha2560000664000401600040160000000010013175113172015621 0ustar cbecbeeb95f0d6fa9245aef891482605fc3219680879d89bdce7825806f6b51775d525pgqd/lib/test/ssl/ca2_complex2.crt0000664000401600040160000000330413175113172015372 0ustar cbecbe-----BEGIN CERTIFICATE----- MIIE0jCCA7qgAwIBAgIUPELvP5YB+z4CjAee4wrc9IRG/W4wDQYJKoZIhvcNAQEL BQAwEjEQMA4GA1UEAxMHVGVzdENBMjAgFw0xMDAxMDEwODA1MDBaGA8yMDYwMTIz MTIzNTUwMFowUDEVMBMGA1UEAxMMY29tcGxleDIuY29tMQ4wDAYDVQQHFAVL9Xp6 5DEnMCUGA1UECB4eadgwBTBqitZOiTCSXxUwTY13MFMwVzBmME0wXzACMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq5NnnkN0y7B0CcaOJ1vLLIAmGdig h2sGSskGHD6NERlviBhscoxaeyIxybrMO94Ly+pvZiKuRgGk7jHARfzk0Z/q69IJ tAjgNoOy9V9HOMTh7zeJbshX5Y4906yFj12EtDfOkWEBJFT+23mWiZLFr8fLGHWj SWeOjGzd5XfI2i5Fp5HB1HTrXOgBq0yO88iYZuY6QIn3uWnQU5LUGiWRzP3apfo3 /PJFV6qO0Nu9ejOiC+dXay/02McU6K3vROh0u+aN7fPGhH95fhwfABSUvyPP+Xdy Gj7bEVEEyZNsEllvcu1uTS9E+XtLZnJHoMvou7E6HEsCuBLyfLB7zCRo0dIs3uRB lJNnwPZXxrlJ2wY4SCug5kbys6/jyrbOvjSe/a1SaN8H0DME9LWyZgd3+Dx1/p0K E+H3r/EboXFT8LN6GOad40gpyoDDs1eE6d2VQy4l0ZuCV6OKs2gFTqJcSlZyK8t0 AXbF91lZzLNATBVXfK2EXAPGVz66sX96r9oAsb8+wPM7huwL1QVBOvkyLT9aFnS6 b9SUdAJhaUGZaqwDR26AYakS3tUZTauo90iqRzZXcbsdeGCJbutmA+/HgTKM778M BVls2VMArqcR3Nyyn9e0lm7lBlEPtvfQzXSeY5QuvDA7Rqx6SX+OlFm9XpZLZOI2 iClfxHLIrpJO1nkCAwEAAaOB3zCB3DAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQE AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUiKLQ93eNsGPvKcIc Z7ogLb+/OF4wHwYDVR0jBBgwFoAUKmTqxYiC5IEF3BVudRQwOSi1oJwwZwYDVR0R BGAwXoIMY29tcGxleDIuY29tghB3d3cuY29tcGxleDIuY29thwR/AAABhxD//gAA AAAAAAAAAAAAAAABhhFodHRwOi8vbG9jYWxob3N0L4ERZm9veGFAZXhhbXBsZS5j b20wDQYJKoZIhvcNAQELBQADggEBAB9FePlTmLVimnG1QdWZT3Cw+4bLz3o5TlFU bv0F7h6jA6jMjdGDw3tdkKRrkSDle8DMX7vn4mdDHpMOuUDsUKP81dNh2+WeII1B ozxa6UMlws0Gez3U6vGIoFCfaBofjld1/24yFdWcURDHLPx3Oxunprah9wmcuya0 T/MQ4sbsMYzKpIPJYCKoZidmStrJsdcayaU5SbTGYwlTyR/be1GKgmyIsvt8sNUV QibIp88yjPjD8HUQDRHY7ZAhAAK05IdRrBmBdN1V0gt44jC8+awGr7B/CC4sqMIQ +x/ATZkly61erS1OKwBzna3LGh40/CBnwdrwvzqiqE5WPpRGKbM= -----END CERTIFICATE----- pgqd/lib/test/ssl/ca1_complex1.crt.sha10000664000401600040160000000005013175113172016216 0ustar cbecbebe8efd15831deec205e0dacc281cd854c42dbffdpgqd/lib/test/ssl/ca2_root.crt.sha2560000664000401600040160000000010013175113172015622 0ustar cbecbebbda471fc545edb28d9c6004cf1147abadd966c17a478c154c311b4ba5d6da28pgqd/lib/test/ssl/ca1_complex1.crt.sha2560000664000401600040160000000010013175113172016366 0ustar cbecbe06d849ab8a640cefbce1d96be4e87ca20e6203056dd7f8626f9792e624a7072bpgqd/lib/test/ssl/ca2_root.crt0000664000401600040160000000224013175113172014622 0ustar cbecbe-----BEGIN CERTIFICATE----- MIIDPDCCAiSgAwIBAgIVAOjbgGPnRtn6CHc1taFKS3ozTQizMA0GCSqGSIb3DQEB CwUAMBIxEDAOBgNVBAMMB1Rlc3RDQTIwIBcNMTAwMTAxMDgwNTAwWhgPMjA2MDEy MzEyMzU1MDBaMBIxEDAOBgNVBAMMB1Rlc3RDQTIwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQCbrBAqJNHLxIV6BwXeBsxMQrM0zlOt6Q5AbgjKFQxnPPBb g6SKIilr58vesdgrOxBKgAD7FDRbV3URpSfK5FmytWztXWUcyh4G0ofO52+AFG8V RES+Uvu0HUA/8Z1dAJX7GbTRUsHxug2TDXZ9n/B73tvK3FkFLaHojma3JFXgPkt8 RxnqylY8cCQyrNOUUDvedskIGXkd9y/VmClXhEZKPdx0cpTDV5odP73hcXDlmkqX aGrZQ1LYXVh9oY8GyUaB3OhcoDno0mWbVng0iGAFsrZgLYjbgAR54oYKKKof/sF7 6VHrQUfHmMCCRSBn9hE4PaK/KOZp/aDxAY7hceNZAgMBAAGjgYYwgYMwEgYDVR0T AQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH AwIGCCsGAQUFBwMBMB0GA1UdDgQWBBQqZOrFiILkgQXcFW51FDA5KLWgnDAfBgNV HSMEGDAWgBQqZOrFiILkgQXcFW51FDA5KLWgnDANBgkqhkiG9w0BAQsFAAOCAQEA XwesDmJPgiL80UVVE0atlhFncVIZLmUL8AR3hgSR8l0/uGpdjdRA9LtyKwv6WeBJ E22bwDb+9TKi1W+VVbTgrZAbQ/I9vCl2Z8RESkkWu++6jHc6nwpBx3ILyMSe6/mx 7qYrJdTmo9TcFlfq+GUjYIOFCRdFr31LUPvfpy0amzcP2vegXMfrmnUP0gDlUNel jBeardoZCNDDUN1DUAL2G9wYw0Fm8s0FhohQEaGW05cwsHh7S9Ah4Plv17oLNU8A KT1hOR7nhPabGyfEST+rieiThaKDu+dqG3Hla9vy9E+92QGoWUqrEz9CVF4p6DNI 25kS4AhgDzl7wxjqWqZVMg== -----END CERTIFICATE----- pgqd/lib/test/ssl/ca1_complex1.crt0000664000401600040160000000201613175113172015367 0ustar cbecbe-----BEGIN CERTIFICATE----- MIIC0TCCAligAwIBAgIVAMMTsZi6qqG9oYv+xw3L3wvkGcHCMAoGCCqGSM49BAMC ME8xEDAOBgNVBAMMB1Rlc3RDQTExCzAJBgNVBAYTAkFBMQ4wDAYDVQQHDAVDaXR5 MTEPMA0GA1UECAwGU3RhdGUxMQ0wCwYDVQQKDARPcmcxMCAXDTEwMDEwMTA4MDUw MFoYDzIwNjAxMjMxMjM1NTAwWjBhMRUwEwYDVQQDDAxjb21wbGV4MS5jb20xEDAO BgNVBAcMB0vDtXp6w6QxNjA0BgNVBAgMLeanmOOAheOBquirluS6ieOCkuW8leOB jei1t+OBk+OBl+OBpuOBjeOBn+OAgjB2MBAGByqGSM49AgEGBSuBBAAiA2IABMH0 0n1ku8n7b26Gvut0gS4VWG7m9AIjRdjNBJZ8GqankFUbwg6Ae1BzsjqFhYPGFMYC gkbAhhaanhacOvKuRWId5pGlbu3y6VLOnP2l0hdAv4BAUDQeBiDJtUXZdYXpLaOB 3zCB3DAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr BgEFBQcDATAdBgNVHQ4EFgQU1OJOZ3u3TajAt32imckJqL7gePIwHwYDVR0jBBgw FoAUr+10yx8qO9p/4QtXF7V0bSXXLqwwZwYDVR0RBGAwXoIMY29tcGxleDEuY29t ghB3d3cuY29tcGxleDEuY29thwR/AAABhxD//gAAAAAAAAAAAAAAAAABhhFodHRw Oi8vbG9jYWxob3N0L4ERZm9veGFAZXhhbXBsZS5jb20wCgYIKoZIzj0EAwIDZwAw ZAIwf+Ni1Sfo37AU14vQMJDug2/NzllDV0MEig+BydhZRJRbCFb+ca3QJaVpRdii 5fF3AjBZSsengBMHFTTWNDuSV2rhzoT5rlnRbUJN7m9vHiGtPa8jA16F+rdo147m zMIj7L4= -----END CERTIFICATE----- pgqd/lib/test/ssl/ca2_client2.crt.sha2560000664000401600040160000000010013175113172016177 0ustar cbecbe3a88d39b34aba2c2cbca1fe4ef715662d24feb93ef62e93c08d2754b50c3d5c1pgqd/lib/test/ssl/ca1_complex1.key0000664000401600040160000000044013175113172015366 0ustar cbecbe-----BEGIN EC PRIVATE KEY----- MIGkAgEBBDBnsHLN5stv4is9F98nfLcmhUrJkirMzr1tzNG/+oImMyTCtKSzhgVo CK52xYxHi9ygBwYFK4EEACKhZANiAATB9NJ9ZLvJ+29uhr7rdIEuFVhu5vQCI0XY zQSWfBqmp5BVG8IOgHtQc7I6hYWDxhTGAoJGwIYWmp4WnDryrkViHeaRpW7t8ulS zpz9pdIXQL+AQFA0HgYgybVF2XWF6S0= -----END EC PRIVATE KEY----- pgqd/lib/test/ssl/ca1_client1.key0000664000401600040160000000030313175113172015173 0ustar cbecbe-----BEGIN EC PRIVATE KEY----- MF8CAQEEGCN63ZhnmnPkMkovjFjH0XyrSb2TSxB5rKAKBggqhkjOPQMBAaE0AzIA BIalAAtRFAhPBXkhkHhmB08vVyw8dEVVVAOxUnOtQq9VAfRaofEqMuE1OA2qj+pa Fw== -----END EC PRIVATE KEY----- pgqd/lib/test/ssl/ca2_server2.crt.sha2560000664000401600040160000000010013175113172016227 0ustar cbecbe670c5c2ebb49c2cf4dc668812023fd674870a15649cce415d86bebf7f0f7031dpgqd/lib/test/ssl/ca2_client2.key0000664000401600040160000000156713175113172015212 0ustar cbecbe-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC4Q52aWwmZbva0hJZWvvHqrCx1eIumscc3d+nYCVcIvKBWHSM3 lqT5wO/RWK4zwQOhs9HF9sfHunLVNRAn5opd1y8PUtTf9Ym6Nf4NF4B4nFDrri5Z 9PyCtg9U7RdeGOKqp/l1lQ2oJwkaLW37KZoyfdwTIyWgWc3bU7ukSkjo6QIDAQAB AoGBAJb9IPnIu3b0zEdjleWDFaycZNnla1mut3WmVwHltWG9XwzxV1hHouvcwyai KizSbm1kcfJro4zwJQS8NH7dlGaoWJ4ROFAApFM1dQCcXc79op8HLWlIr6zj3Xoo iQpYeR6oJSH3VBqtyqNMAaEHjwEAVSaJgkhVsk85Uvle42IBAkEA5cv2PExJK28I HAihJTQcJmGHn7aEjEE2FKUH96ftldnfHVX5vYnVnQjerJLNOCSdbtkh2q32j97B yQEwSjPlYQJBAM1GgNa9Nh5kxr5m2TXRFqBHI7Y8y9sAszXGBy26emkaqodLC4JK EJjUZiVyySSg7l4f7AlDEuvfTpKB2IwsKIkCQBbdcZXUXV91/8+SSD6Ebdyfzbfl mFDAWFo7hYxt6CUF9b5chKoIYfnrHQT84nCYrBWQg17X+nWcS5nthVmMymECQHOg 5yoO4tE0Cw9td6Ts5kXaTK92h44RUuFTbu+/Pvy9XJlhGFaHydmMqw0lH1BWpm62 pIqDEcFzxBqasZQju6ECQE5lgnj4csDkSeojlI7E1B+LGePQ54Rde0Qqddi4aCXy NV11N30tLnfRbUSlkKDRp0ybUHBqrB0RrgGB3GbaA2g= -----END RSA PRIVATE KEY----- pgqd/lib/test/ssl/ca1_root.crt0000664000401600040160000000157313175113172014631 0ustar cbecbe-----BEGIN CERTIFICATE----- MIICZjCCAeygAwIBAgIUcpNzMlepBYsYpRJ5pQGlPxvxwp4wCgYIKoZIzj0EAwIw TzEQMA4GA1UEAwwHVGVzdENBMTELMAkGA1UEBhMCQUExDjAMBgNVBAcMBUNpdHkx MQ8wDQYDVQQIDAZTdGF0ZTExDTALBgNVBAoMBE9yZzEwIBcNMTAwMTAxMDgwNTAw WhgPMjA2MDEyMzEyMzU1MDBaME8xEDAOBgNVBAMMB1Rlc3RDQTExCzAJBgNVBAYT AkFBMQ4wDAYDVQQHDAVDaXR5MTEPMA0GA1UECAwGU3RhdGUxMQ0wCwYDVQQKDARP cmcxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEyc1kR5c2Hh0KRhv8QH/IwBn1HVTm 1bgJSGYQV4cNvRBEKxiygHYBpkO1XbBSQ+UlhdbEiKyr6mq6Y1rxmQjeZ/uw3C/Q 5QUoLXrnSI0EezY2OJ6eMhzeSq40PPxH0LNjo4GGMIGDMBIGA1UdEwEB/wQIMAYB Af8CAQEwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEF BQcDATAdBgNVHQ4EFgQUr+10yx8qO9p/4QtXF7V0bSXXLqwwHwYDVR0jBBgwFoAU r+10yx8qO9p/4QtXF7V0bSXXLqwwCgYIKoZIzj0EAwIDaAAwZQIwcSWiMajsAebd KiE63aLpQAF99v9SlAVoR2icOR5wQ/iwSCNK9bvzAJ0vH1scY7csAjEArmrBllPj ZLhACzvYd+z2Ef8VfRNOmhykq+Ltf0gBE5t7bTwHXeoRp+oG+Ermcako -----END CERTIFICATE----- pgqd/lib/test/ssl/ca2_root.key0000664000401600040160000000321313175113172014623 0ustar cbecbe-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAm6wQKiTRy8SFegcF3gbMTEKzNM5TrekOQG4IyhUMZzzwW4Ok iiIpa+fL3rHYKzsQSoAA+xQ0W1d1EaUnyuRZsrVs7V1lHMoeBtKHzudvgBRvFURE vlL7tB1AP/GdXQCV+xm00VLB8boNkw12fZ/we97bytxZBS2h6I5mtyRV4D5LfEcZ 6spWPHAkMqzTlFA73nbJCBl5Hfcv1ZgpV4RGSj3cdHKUw1eaHT+94XFw5ZpKl2hq 2UNS2F1YfaGPBslGgdzoXKA56NJlm1Z4NIhgBbK2YC2I24AEeeKGCiiqH/7Be+lR 60FHx5jAgkUgZ/YROD2ivyjmaf2g8QGO4XHjWQIDAQABAoIBAF3aC1jNH+rAjVQ3 XbMw2AR2BgKlDdAAfMYdUJE8xWCMXcBeill5JQ3YDpIxGElf739q0qyMzs/u1zaP IoFw0B8swAIO14HW3OlyDcyQvSkzqJDiuqsf7n31UKH8ccn7K3DaW6gDQ8gPSlXI vRIZ1etoPYx88fJsQ3uoNobdkiSBmVboC10k9N73ZQELpHhfAHbX47aca2Wlgk4n Xy16j2JxYVuLKGgUYu0EjLhcQzRRsnYONMmICvutjmrGvWguR/wG85slstHKqPIf lCdvVQbOIRmjGfwz+OTjvk3Y3pOqKFuNM50ZclS30lsaFLlsRxocL6Eei/PEHFMF VcUFGK0CgYEAyn2x9SCkf5b7FUm44Mszcp5OBsApbq3rhpgWnQ0rJwZ+0X5lgECu Knzv0l/Lai3XKfdpUe/WWpsskL/CBfv6n0BKmjs1w7OcYke243bXK31zF8X+5M5a dPSPl6efLsIhx8TLVhacMuurlhMlp7Z8f9XF3qrQTnG0HTzZRdpmeJcCgYEAxM8e sg+inxZtUNbsb4XyJ21KhLmsPgZTLczx4eNh22NwBhA3n3frsUBiac5lFoRPu/I4 Q67KOLp4EgK9WzcdNMk1KZf4bzNeJ0Sqe9W2xjqypuqnFCwNhaq0cShNAJG5eCBk 53BJunQg5tmWIf+JTxmOMTGf3U1GR9+2erUxkY8CgYEAtikfh6KG+ZUMaZk5rVh/ /vO0w9PpMf1Z7WoypbRsBp6MRdBMZXVKeUSNxpaQ+wMkFkfRwnldP4NrMwie9h05 2DvRITB07IpO/AcB211UsnYXrOJcrN+Kdr5v0wFjbYANPZanSdnrSM/EO60b6eV7 2NCqQecun/MgElFkMLgKCR8CgYBKGwce2v5L0uEEkHjkoVYF/IQTp+owTKdjVok5 minNtwqGbfLTzA7mZizoVDwCF34Ccfk3mwgwBh6SAZAVWZJYvu6So9N646evN47O JDG3QIDmkvKoiNHqo9VfymH8NeLVVbVI5CIO4A1Hf2Sllpe4PhIpedsNNDyjcZw/ ZhytBwKBgEYHg2YkUqx/7eIqgbJWOwXHJ6PMieuVQdGRgtZZUyQmfXWxfA6qew3S OdWwT0G7pnhKXiRNSkvqEk1vv3Vz/dMpWU+XHTM9D1BQiHNzKou8px6wzlm4bl4h B/+h4EhODff20KWew+jrEgvxtvdy3bXfx/97eGTtjb/1+8H+VJvx -----END RSA PRIVATE KEY----- pgqd/lib/test/ssl/ca1_server1.crt0000664000401600040160000000152313175113172015230 0ustar cbecbe-----BEGIN CERTIFICATE----- MIICRzCCAcygAwIBAgIUaGoZAQJJgI38t7avoXQ0kr04edUwCgYIKoZIzj0EAwIw TzEQMA4GA1UEAwwHVGVzdENBMTELMAkGA1UEBhMCQUExDjAMBgNVBAcMBUNpdHkx MQ8wDQYDVQQIDAZTdGF0ZTExDTALBgNVBAoMBE9yZzEwIBcNMTAwMTAxMDgwNTAw WhgPMjA2MDEyMzEyMzU1MDBaMBYxFDASBgNVBAMMC3NlcnZlcjEuY29tMHYwEAYH KoZIzj0CAQYFK4EEACIDYgAEqKEl7by59G1PKs2554BGvg2oSHc348OAJwdQLkfc 7hX11Jwxp74PlhiYXZpkh388G1q+kGhi6WCIvSwKhzz30AGinB/rOjhWHNPdK07s UnvIPp5waQE0Z1kVCb5LRYqLo4GfMIGcMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/ BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBQT+dMVA/h4Ljhq cveTByTRXC1BmTAfBgNVHSMEGDAWgBSv7XTLHyo72n/hC1cXtXRtJdcurDAnBgNV HREEIDAeggtzZXJ2ZXIxLmNvbYIPd3d3LnNlcnZlcjEuY29tMAoGCCqGSM49BAMC A2kAMGYCMQCVPNh7JE8a4gVXB0xg4rB8pCchZ+jPlALZ5CovTLVNkETrYs3HuZ8E eSIZ6emd8xMCMQCpYslvy17e6QniFJpMPbi0xw+GBqWPZ47cdAlowbdYL+YEoSRG e3Rj4z3IILJSieM= -----END CERTIFICATE----- pgqd/lib/test/ssl/mkcerts.py0000775000401600040160000002327313175113172014436 0ustar cbecbe#! /usr/bin/env python # -*- coding: utf-8 -*- """Generate x509 keys and certs. """ from __future__ import absolute_import, division, print_function, unicode_literals from cryptography import x509 import ipaddress import datetime import uuid # # CA utilities # def set_string_mask(mask): """Set default string format in certs. utf8only - as name says default - anything: usually printable, t61, bmp or utf8 according to data. pkix - "default" without t61 nombstr - "default" without bmp and utf8 MASK: - bitmask as integer """ if isinstance(mask, unicode): mask = mask.encode('utf8') from cryptography.hazmat.backends.openssl import backend backend._lib.ASN1_STRING_set_default_mask_asc(mask) def get_backend(): from cryptography.hazmat.backends import default_backend return default_backend() def new_key(keydesc): from cryptography.hazmat.primitives.asymmetric import ec, rsa t, v = keydesc.split(':') if t == 'ec': curve = getattr(ec, v.upper()) return ec.generate_private_key(curve=curve, backend=get_backend()) elif t == 'rsa': return rsa.generate_private_key(key_size=int(v), public_exponent=65537, backend=get_backend()) else: raise Exception('Bad key type') def _load_name(vals): from cryptography.x509.oid import NameOID name_map = { 'CN': NameOID.COMMON_NAME, 'C': NameOID.COUNTRY_NAME, 'L': NameOID.LOCALITY_NAME, 'ST': NameOID.STATE_OR_PROVINCE_NAME, 'O': NameOID.ORGANIZATION_NAME, 'OU': NameOID.ORGANIZATIONAL_UNIT_NAME, 'serial': NameOID.SERIAL_NUMBER, 'SN': NameOID.SURNAME, 'GN': NameOID.GIVEN_NAME, 'T': NameOID.TITLE, 'GQ': NameOID.GENERATION_QUALIFIER, 'DQ': NameOID.DN_QUALIFIER, 'P': NameOID.PSEUDONYM, 'DC': NameOID.DOMAIN_COMPONENT, 'E': NameOID.EMAIL_ADDRESS, } attlist = [] for r in vals: k, v = r.split('=', 1) oid = name_map[k] n = x509.NameAttribute(oid, v) attlist.append(n) return x509.Name(attlist) def _load_alt_names(alt_names): from cryptography.x509.general_name import RFC822Name, DNSName, IPAddress, UniformResourceIdentifier, RegisteredID gnames = [] for alt in alt_names: t, val = alt.split(':', 1) if t == '822': gn = RFC822Name(val) elif t == 'dns': gn = DNSName(val) elif t == 'ip4': gn = IPAddress(ipaddress.IPv4Address(val)) elif t == 'ip6': gn = IPAddress(ipaddress.IPv6Address(val)) elif t == 'i4n': gn = IPAddress(ipaddress.IPv4Network(val)) elif t == 'i6n': gn = IPAddress(ipaddress.IPv6Network(val)) elif t == 'uri': gn = UniformResourceIdentifier(val) elif t == 'rid': gn = RegisteredID(val) else: raise Exception('Invalid altname: '+alt) gnames.append(gn) return x509.SubjectAlternativeName(gnames) # why no defaults? def _wrapKeyUsage(digital_signature=False, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False): return x509.KeyUsage(digital_signature=digital_signature, content_commitment=content_commitment, key_encipherment=key_encipherment, data_encipherment=data_encipherment, key_agreement=key_agreement, key_cert_sign=key_cert_sign, crl_sign=crl_sign, encipher_only=encipher_only, decipher_only=decipher_only) def x509_sign(privkey, pubkey, subject, issuer, ca=False, alt_names=None, usage=None): from cryptography.hazmat.primitives import hashes from cryptography.x509.oid import ExtendedKeyUsageOID import hashlib dt_start = datetime.datetime(2010, 1, 1, 8, 5, 0) dt_end = datetime.datetime(2060, 12, 31, 23, 55) #serial = int(uuid.uuid4()) serial = int(hashlib.sha1(subject[0]).hexdigest(), 16) builder = (x509.CertificateBuilder() .subject_name(_load_name(subject)) .issuer_name(_load_name(issuer)) .not_valid_before(dt_start) .not_valid_after(dt_end) .serial_number(serial) .public_key(pubkey)) # BasicConstraints, critical if ca: ext = x509.BasicConstraints(ca=True, path_length=1) else: ext = x509.BasicConstraints(ca=False, path_length=None) builder = builder.add_extension(ext, critical=True) # KeyUsage, critical if ca: ext = _wrapKeyUsage(digital_signature=True, key_cert_sign=True, crl_sign=True) else: ext = _wrapKeyUsage(digital_signature=True, key_encipherment=True) builder = builder.add_extension(ext, critical=True) # ExtendedKeyUsage if not usage and ca: usage = ['client', 'server'] if usage: usage_map = { 'server': ExtendedKeyUsageOID.SERVER_AUTH, 'client': ExtendedKeyUsageOID.CLIENT_AUTH, 'code': ExtendedKeyUsageOID.CODE_SIGNING, 'email': ExtendedKeyUsageOID.EMAIL_PROTECTION, 'time': ExtendedKeyUsageOID.TIME_STAMPING, 'ocsp': ExtendedKeyUsageOID.OCSP_SIGNING, } xlist = [usage_map[x] for x in usage] ext = x509.ExtendedKeyUsage(xlist) builder = builder.add_extension(ext, critical=False) # SubjectKeyIdentifier ext = x509.SubjectKeyIdentifier.from_public_key(pubkey) builder = builder.add_extension(ext, critical=False) # AuthorityKeyIdentifier ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(privkey.public_key()) builder = builder.add_extension(ext, critical=False) # SubjectAlternativeName if alt_names: ext = _load_alt_names(alt_names) builder = builder.add_extension(ext, critical=False) # final cert cert = builder.sign(private_key=privkey, algorithm=hashes.SHA256(), backend=get_backend()) return cert class Base: def write_key(self, fn): from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption data = self.key.private_bytes(encoding = Encoding.PEM, format = PrivateFormat.TraditionalOpenSSL, encryption_algorithm = NoEncryption()) with open(fn, 'w') as f: f.write(data) def write_cert(self, fn): from cryptography.hazmat.primitives.serialization import Encoding data = self.cert.public_bytes(Encoding.PEM) with open(fn, 'w') as f: f.write(data) def write_fp(self, pfx): from cryptography.hazmat.primitives import hashes h_sha1 = self.cert.fingerprint(hashes.SHA1()) open(pfx+'.sha1', 'w').write(h_sha1.encode('hex')) h_sha256 = self.cert.fingerprint(hashes.SHA256()) open(pfx+'.sha256', 'w').write(h_sha256.encode('hex')) def write(self, pfx): self.write_key(pfx+'.key') self.write_cert(pfx+'.crt') self.write_fp(pfx+'.crt') class CA(Base): def __init__(self, keydesc, name, master_ca=None, usage=None): self.key = new_key(keydesc) self.name = name if master_ca: self.cert = master_ca.sign(self.key.public_key(), name, ca=True, usage=usage) else: self.cert = x509_sign(self.key, self.key.public_key(), name, name, ca=True, usage=usage) def sign(self, pubkey, subject_name, alt_names=None, ca=False, usage=None): return x509_sign(self.key, pubkey, subject_name, self.name, ca=ca, alt_names=alt_names, usage=usage) class Leaf(Base): def __init__(self, master_ca, keydesc, name, alt_names=None, usage=None): self.key = new_key(keydesc) self.name = name self.cert = master_ca.sign(self.key.public_key(), name, alt_names=alt_names, usage=usage) # # CA1 - EC keys # set_string_mask("utf8only") ca1 = CA('ec:secp384r1', ['CN=TestCA1', 'C=AA', 'L=City1', 'ST=State1', 'O=Org1']) ca1.write('ca1_root') server1 = Leaf(ca1, 'ec:secp384r1', ['CN=server1.com'], ['dns:server1.com', 'dns:www.server1.com'], usage=['server']) server1.write('ca1_server1') client1 = Leaf(ca1, 'ec:secp192r1', ['CN=client1'], ['822:client@company.com'], usage=['client']) client1.write('ca1_client1') complex1 = Leaf(ca1, 'ec:secp384r1', name = ['CN=complex1.com', 'L=Kõzzä', 'ST=様々な論争を引き起こしてきた。'], #name = ['CN=complex1.com', 'C=QQ', 'L=Loc1', 'ST=Foo', 'O=Aorg2', 'OU=Unit1'], alt_names = ['dns:complex1.com', 'dns:www.complex1.com', 'ip4:127.0.0.1', 'ip6:fffe::1', #'i4n:192.168.1.0/24', 'i6n:::1/128', 'uri:http://localhost/', '822:fooxa@example.com'], usage=['server']) complex1.write('ca1_complex1') set_string_mask("utf8only") # # CA2 - RSA keys # ca2 = CA('rsa:2048', ['CN=TestCA2']) ca2.write('ca2_root') server2 = Leaf(ca2, 'rsa:2048', ['CN=server2.com'], ['dns:server2.com', 'dns:www.server2.com'], usage=['server']) server2.write('ca2_server2') client2 = Leaf(ca2, 'rsa:1024', name = ['CN=client2', 'C=XX', 'L=City2', 'ST=State2', 'O=Org2'], alt_names = ['822:client2@company.com'], usage=['client']) client2.write('ca2_client2') # create cert with old string types set_string_mask("default") complex2 = Leaf(ca2, 'rsa:4096', name = ['CN=complex2.com', 'L=Kõzzä', 'ST=様々な論争を引き起こしてきた。'], alt_names = ['dns:complex2.com', 'dns:www.complex2.com', 'ip4:127.0.0.1', 'ip6:fffe::1', 'uri:http://localhost/', '822:fooxa@example.com'], usage=['server']) complex2.write('ca2_complex2') set_string_mask("utf8only") pgqd/lib/test/ssl/ca2_complex2.crt.sha2560000664000401600040160000000010013175113172016370 0ustar cbecbe920813a85cf27e73c8f0ceeba2ba54e34c2e6a6df0260379f3c1bb1440b1ab00pgqd/lib/test/ssl/ca1_root.crt.sha10000664000401600040160000000005013175113172015451 0ustar cbecbecd323d7b394594cbaefabcdfa240c1a0c8b4144cpgqd/lib/test/ssl/ca2_client2.crt.sha10000664000401600040160000000005013175113172016027 0ustar cbecbe7a172eea2f90c57a24653ef39218c1f75fd12119pgqd/lib/test/ssl/ca1_server1.crt.sha10000664000401600040160000000005013175113172016055 0ustar cbecbe0fc044d1e14c9a2ea1d35a78fac85251acaafd79pgqd/lib/test/ssl/ca2_complex2.crt.sha10000664000401600040160000000005013175113172016220 0ustar cbecbe95abcedb5c2e8bde0ed6bc079b4750985644cad1pgqd/lib/test/ssl/ca2_complex2.key0000664000401600040160000000625313175113172015400 0ustar cbecbe-----BEGIN RSA PRIVATE KEY----- MIIJKAIBAAKCAgEAq5NnnkN0y7B0CcaOJ1vLLIAmGdigh2sGSskGHD6NERlviBhs coxaeyIxybrMO94Ly+pvZiKuRgGk7jHARfzk0Z/q69IJtAjgNoOy9V9HOMTh7zeJ bshX5Y4906yFj12EtDfOkWEBJFT+23mWiZLFr8fLGHWjSWeOjGzd5XfI2i5Fp5HB 1HTrXOgBq0yO88iYZuY6QIn3uWnQU5LUGiWRzP3apfo3/PJFV6qO0Nu9ejOiC+dX ay/02McU6K3vROh0u+aN7fPGhH95fhwfABSUvyPP+XdyGj7bEVEEyZNsEllvcu1u TS9E+XtLZnJHoMvou7E6HEsCuBLyfLB7zCRo0dIs3uRBlJNnwPZXxrlJ2wY4SCug 5kbys6/jyrbOvjSe/a1SaN8H0DME9LWyZgd3+Dx1/p0KE+H3r/EboXFT8LN6GOad 40gpyoDDs1eE6d2VQy4l0ZuCV6OKs2gFTqJcSlZyK8t0AXbF91lZzLNATBVXfK2E XAPGVz66sX96r9oAsb8+wPM7huwL1QVBOvkyLT9aFnS6b9SUdAJhaUGZaqwDR26A YakS3tUZTauo90iqRzZXcbsdeGCJbutmA+/HgTKM778MBVls2VMArqcR3Nyyn9e0 lm7lBlEPtvfQzXSeY5QuvDA7Rqx6SX+OlFm9XpZLZOI2iClfxHLIrpJO1nkCAwEA AQKCAgAbQ4sdnP58N4fY3dJM8sswQRTM2p13p0wSqirsZgwogXA1ohvpf+AI3QAo XCSEBt69An29LxmeSjiZGRLCjFMX9F9yJaghEZ//SaIwV3mLzc1RI/QUxIUjFx0/ +5QlmeiycoPCmjWCPndZDNMc/VOogQkG0/qkeW4vZkVEZ2plMSEkZYb1abbUxpEt VTcKCgAdqcvr4YCZ1/Sgsx49IWcvuc76vO4UpbLbIWEcUy6isn23goVjpPU1VT0/ VBRHUODr0tEO1MPg3hjCL1nFu39/eANeUtqwzPgmokcueNydddS94Hi8jxxYwHzU xKgZJw3OdXUUdd7yvSAHLEwfvlFhc3CdgcQCLUpMmLWSAXdMX3tbQuFDN9jdr5u8 PP9b4our1N1tONBJSiVmOAzTdjPFVgW34i04MKI15RbRM+5X29Za0E9XQeu8YkA0 s5sLw88Mu0HiAF2yBu1SU+bMpef3gGVCJdqQyMxHkhAlvJcA+9Hi9DBOBeu4jpgr JuNwghW5apuDJ3dsvAYu/wCBCeTK0ODfxfg+SSY83EcLD3xY8SvPo2JFiIvjjW4d NAt4tzlxGj/l9zy/025ULuUSS0SBe3RIZxDcKsvCmh/h0Brby26/0zuNPcHscy13 xRlKTiZuALd5F3uO/t6uAMWe+ZM99Mib/eeWqvA3rV3ZO2Jf2QKCAQEA13rE13WB AtjCsiFZMZxhNXIsQSGVWSgV5ufmw47szm7cp0zQHO2AhusvYWo+jPYl2OB8BHZa p2OMcPlFMVLSMSOEyvyW9UqJi1pWrxNtvTgKad5imFrm9tMHCG0MpDqUuiq2Scfs lPLQOKw9ZB9sctfXuR/dS5N3i/AiE2Rghnj+Ctt3/xFFLJVgVOZ14jTKstUF30lk iVzqZHOxUwJlplKQlrsuA2HiVVjommRBQWmcF0ZnUR7zEgDZ66OKNuKQe1SSFx8p gqHQRhlym0250/qwAdeM/VAGg8/Zqha7G2Y0HmkQsiJG9TOqUVC7lt9jfMnOAITj qDdh937JO1kwnwKCAQEAy9cZc13NOja+3lbodxO9/NT81pKiqYyIb53w0DRwcu67 F5eD23vZ6ZawZg3COS+t3WNQvuX3hiKBnmbwEc0Ch7Efq3tajOk6Btm6kSU+/mup sLJJWJwMgthi16CBAR2YGlGGPouE2HUXiLeYI/4sggdR0MQ2luur+DGdjj3zjDYN Rjx//nmzrQDg0mOFKoMk9CqV/uYycZoDlOJ5349jeq75PB4RmvBCOyzWF8prhuFH 9say0vFqtEzn3Ef6LlQuaxHsMzoET1ixfbldk5dmSLeo/Vufwb2nOrEdyys2yxEL 7156Zx8ujyphPynXzDP7MoRfaUzXU+eSFUim62Wp5wKCAQBqoianOU8OyZYP8NDi 7DBu8oFT+fqelGfCTSC1xhUKU3J+mFSk/COOoPRhkOJDf8xa9zxoOOnDzE/D2JeV QRcjmiAtE7Lg0HIgtCQRn9u+t+zvV04Td2i03NmuIckPvK29M0XiXT2GuGKyqQn4 clSIwKfQxSQOMnwGlF9ImXPrGBUf6lwHrdQ9q+5wFVxhBR8KQMEXLorCL2kPLPGj 0pvbIyDKdvbeVaJb+hOh09s3M7w7CGw4iMhhMGqnYdcUMiqt3bwuFQ3Fx636Ar3k fSJo9LYFM1ZiRSf0MvfUZEtT09AW/Id2n9/dmCPA+eLbVtE0/+kh+KFwrXedsvCC njChAoIBAQCWoQv4mY9nfj8LdDNykw8o/4cbVN6I5gWIXbEvWiDifZGZvGg+z8XW /Hi74Vzt/1K+r35iovOHloYsNKsgrFs7RGM/x/bA/DPaDhj02w4y2gJaRVU3+pCw PBDGgJ/z4BefEzFdlKIFhnlumST/K/td1uTZnbUtT1gUHy3rh9IqgWLLzTdUKEDj DiwUrGAiayjqmXGgHCGYmxQceENRNddwOE6K4zllQjffHKHUkMtmr6ELNge9I5MT S2Vm/XVduO1YR7VAoZW/wK3gVxNUVmPKRpXFK2jhtTEXiPyYPwXNh3pHV4yiGYPB XbLhBx2lk6AD05fh61uPcCcePULD3qtvAoIBAC7U8xSpIetJd2agBg5fTdakYo5B wtMHsc5ShyrkNmBziD9g1lJTFRhfHYgvSZkRl3BznVX4gwq4vn2ZRKujI80yaFEh KkS4Brxr520lqfC1PdHu5iomGi3nAbBFANwn6UoI0j/PNViGBs1IAa3c32NuTaBE I5Z92qhxVsLBx92fmXsIb7//mObmOroj/zEqLAAu892POktQHnBGTwixg2DUKH1C s/xKOzJLO2fX2d4YjNv81NsNxnLE1hO8Ueky16Bf17RhzPufOtePRjIGnM2smsrq rRh0ciSccKOfpVFLySllJpWqdRH+raEVXNvA7VeDx0HG/0hqhk2GJaYqxqs= -----END RSA PRIVATE KEY----- pgqd/lib/test/ssl/ca1_server1.crt.sha2560000664000401600040160000000010013175113172016225 0ustar cbecbecdf2aed621058e9b32a5079118156ea5734af086445dae3ad7b29729ceddd0a6pgqd/lib/test/Makefile0000664000401600040160000000367313175113172013252 0ustar cbecbe AM_FEATURES = libusual SUBLOC = test DIST_SUBDIRS = attregex USUAL_DIR = $(abs_top_srcdir) regtest_system_SOURCES = \ test_aatree.c \ test_base.c \ test_bits.c \ test_cbtree.c \ test_cfparser.c \ test_crypto.c \ test_ctype.c \ test_cxalloc.c \ test_endian.c \ test_event.c \ test_fileutil.c \ test_fnmatch.c \ test_getopt.c \ test_hashing.c \ test_hashtab.c \ test_heap.c \ test_json.c \ test_list.c \ test_mdict.c \ test_netdb.c \ test_pgutil.c \ test_psrandom.c \ test_regex.c \ test_shlist.c \ test_socket.c \ test_string.c \ test_strpool.c \ test_talloc.c \ test_time.c \ test_tls.c \ test_utf8.c \ test_wchar.c \ \ test_common.c test_common.h \ tinytest.c tinytest.h tinytest_macros.h # build regtest_system against actual library regtest_system_LDADD = -static ../libusual.la regtest_system_LIBS = $(TLS_LIBS) $(LIBS) regtest_system_LDFLAGS = $(TLS_LDFLAGS) regtest_system_CPPFLAGS = -I.. -I. $(TLS_CPPFLAGS) # build regtest_compat as embedded project regtest_compat_EMBED_LIBUSUAL = 1 regtest_compat_CPPFLAGS := -I.. -I. -DUSUAL_TEST_CONFIG $(TLS_CPPFLAGS) regtest_compat_LIBS = $(TLS_LIBS) $(LIBS) regtest_compat_LDFLAGS = $(TLS_LDFLAGS) regtest_compat_SOURCES := $(regtest_system_SOURCES) nodist_regtest_compat_SOURCES = test_config.h connect_SOURCES = connect-tls.c connect_LDADD = -static ../libusual.la connect_LIBS = $(TLS_LIBS) $(LIBS) connect_LDFLAGS = $(TLS_LDFLAGS) connect_CPPFLAGS = -I.. -I. $(TLS_CPPFLAGS) EXTRA_DIST = Makefile tinytest_demo.c force_compat.sed test_cfparser.ini noinst_PROGRAMS = regtest_system connect EXTRA_PROGRAMS = regtest_compat include ../build.mk test_config.h: force_compat.sed ../usual/config.h $(E) " GEN-COMPAT" $@ $(Q) sed -f $^ > $@ clean: clean-local clean-local: $(Q) $(RM) -r fmod_test run: @echo CC=$(CC) CFLAGS=$(CFLAGS) $(MAKE) -C .. $(MAKE) all regtest_system regtest_compat ./regtest_system ./regtest_compat .PHONY: tags tags: ctags $(regtest_system_SOURCES) pgqd/lib/test/tinytest.c0000664000401600040160000002257113175113172013637 0ustar cbecbe/* tinytest.c -- Copyright 2009 Nick Mathewson * * 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 #ifdef WIN32 #include #include #else #include #include #include #endif #include #define evutil_snprintf snprintf #include "tinytest.h" #include "tinytest_macros.h" #define LONGEST_TEST_NAME 16384 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ static int n_ok = 0; /**< Number of tests that have passed */ static int n_bad = 0; /**< Number of tests that have failed. */ static int n_skipped = 0; /**< Number of tests that have been skipped. */ static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ const char *verbosity_flag = ""; enum outcome { SKIP=2, OK=1, FAIL=0 }; static enum outcome cur_test_outcome = 0; const char *cur_test_prefix = NULL; /**< prefix of the current test group */ /** Name of the current test, if we haven't logged is yet. Used for --quiet */ const char *cur_test_name = NULL; #ifdef WIN32 /** Pointer to argv[0] for win32. */ static const char *commandname = NULL; #endif static enum outcome _testcase_run_bare(const struct testcase_t *testcase) { void *env = NULL; int outcome; if (testcase->setup) { env = testcase->setup->setup_fn(testcase); if (!env) return FAIL; else if (env == (void*)TT_SKIP) return SKIP; } cur_test_outcome = OK; testcase->fn(env); outcome = cur_test_outcome; if (testcase->setup) { if (testcase->setup->cleanup_fn(testcase, env) == 0) outcome = FAIL; } return outcome; } #define MAGIC_EXITCODE 42 static enum outcome _testcase_run_forked(const struct testgroup_t *group, const struct testcase_t *testcase) { #ifdef WIN32 /* Fork? On Win32? How primitive! We'll do what the smart kids do: we'll invoke our own exe (whose name we recall from the command line) with a command line that tells it to run just the test we want, and this time without forking. (No, threads aren't an option. The whole point of forking is to share no state between tests.) */ int ok; char buffer[LONGEST_TEST_NAME+256]; STARTUPINFO si; PROCESS_INFORMATION info; DWORD exitcode; if (!in_tinytest_main) { printf("\nERROR. On Windows, _testcase_run_forked must be" " called from within tinytest_main.\n"); abort(); } if (opt_verbosity>0) printf("[forking] "); evutil_snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", commandname, verbosity_flag, group->prefix, testcase->name); memset(&si, 0, sizeof(si)); memset(&info, 0, sizeof(info)); si.cb = sizeof(si); ok = CreateProcess(commandname, buffer, NULL, NULL, 0, 0, NULL, NULL, &si, &info); if (!ok) { printf("CreateProcess failed!\n"); return 0; } WaitForSingleObject(info.hProcess, INFINITE); GetExitCodeProcess(info.hProcess, &exitcode); CloseHandle(info.hProcess); CloseHandle(info.hThread); if (exitcode == 0) return OK; else if (exitcode == MAGIC_EXITCODE) return SKIP; else return FAIL; #else int outcome_pipe[2]; pid_t pid; (void)group; if (pipe(outcome_pipe)) perror("opening pipe"); if (opt_verbosity>0) printf("[forking] "); pid = fork(); if (!pid) { /* child. */ int test_r, write_r; char b[1]; close(outcome_pipe[0]); test_r = _testcase_run_bare(testcase); assert(0<=(int)test_r && (int)test_r<=2); b[0] = "NYS"[test_r]; write_r = write(outcome_pipe[1], b, 1); if (write_r != 1) { perror("write outcome to pipe"); exit(1); } exit(0); } else { /* parent */ int status, r; char b[1]; /* Close this now, so that if the other side closes it, * our read fails. */ close(outcome_pipe[1]); r = read(outcome_pipe[0], b, 1); if (r == 0) { printf("[Lost connection!] "); return 0; } else if (r != 1) { perror("read outcome from pipe"); } waitpid(pid, &status, 0); close(outcome_pipe[0]); return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL); } #endif } int testcase_run_one(const struct testgroup_t *group, const struct testcase_t *testcase) { enum outcome outcome; if (testcase->flags & TT_SKIP) { if (opt_verbosity>0) printf("%s%s: SKIPPED\n", group->prefix, testcase->name); ++n_skipped; return SKIP; } if (opt_verbosity>0 && !opt_forked) { printf("%s%s: ", group->prefix, testcase->name); } else { if (opt_verbosity==0) printf("."); cur_test_prefix = group->prefix; cur_test_name = testcase->name; } if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) { outcome = _testcase_run_forked(group, testcase); } else { outcome = _testcase_run_bare(testcase); } if (outcome == OK) { ++n_ok; if (opt_verbosity>0 && !opt_forked) puts(opt_verbosity==1?"OK":""); } else if (outcome == SKIP) { ++n_skipped; if (opt_verbosity>0 && !opt_forked) puts("SKIPPED"); } else { ++n_bad; if (!opt_forked) printf("\n [%s FAILED]\n", testcase->name); } if (opt_forked) { exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1)); } else { return (int)outcome; } } int _tinytest_set_flag(struct testgroup_t *groups, const char *arg, unsigned long flag) { int i, j; int length = LONGEST_TEST_NAME; char fullname[LONGEST_TEST_NAME]; int found=0; if (strstr(arg, "..")) length = strstr(arg,"..")-arg; for (i=0; groups[i].prefix; ++i) { for (j=0; groups[i].cases[j].name; ++j) { evutil_snprintf(fullname, sizeof(fullname), "%s%s", groups[i].prefix, groups[i].cases[j].name); if (!flag) /* Hack! */ printf(" %s\n", fullname); if (!strncmp(fullname, arg, length)) { groups[i].cases[j].flags |= flag; ++found; } } } return found; } static void usage(struct testgroup_t *groups, int list_groups) { puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); puts(" Specify tests by name, or using a prefix ending with '..'"); puts(" Use --list-tests for a list of tests."); if (list_groups) { puts("Known tests are:"); _tinytest_set_flag(groups, "..", 0); } exit(0); } int tinytest_main(int c, const char **v, struct testgroup_t *groups) { int i, j, n=0; #ifdef WIN32 commandname = v[0]; #endif for (i=1; i= 1) printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped); return (n_bad == 0) ? 0 : 1; } int _tinytest_get_verbosity(void) { return opt_verbosity; } void _tinytest_set_test_failed(void) { if (opt_verbosity <= 0 && cur_test_name) { if (opt_verbosity==0) puts(""); printf("%s%s: ", cur_test_prefix, cur_test_name); cur_test_name = NULL; } cur_test_outcome = 0; } void _tinytest_set_test_skipped(void) { if (cur_test_outcome==OK) cur_test_outcome = SKIP; } pgqd/lib/test/test_ctype.c0000664000401600040160000000204413175113172014130 0ustar cbecbe#include #include #include "test_common.h" #include /* * if char works */ static void test_ctype_char(void *p) { int c, cx; for (c = 0; c < 256; c++) { cx = (int)(char)c; int_check(isalnum(c), isalnum(cx)); int_check(isalpha(c), isalpha(cx)); int_check(isascii(c), isascii(cx)); int_check(isblank(c), isblank(cx)); int_check(iscntrl(c), iscntrl(cx)); int_check(isdigit(c), isdigit(cx)); int_check(islower(c), islower(cx)); int_check(isgraph(c), isgraph(cx)); int_check(isprint(c), isprint(cx)); int_check(ispunct(c), ispunct(cx)); int_check(isspace(c), isspace(cx)); int_check(isupper(c), isupper(cx)); int_check(isxdigit(c), isxdigit(cx)); if (c == 255) { int_check(toupper(c), (unsigned char)toupper(cx)); int_check(tolower(c), (unsigned char)tolower(cx)); } else { int_check(toupper(c), toupper(cx)); int_check(tolower(c), tolower(cx)); } } end:; } /* * Describe */ struct testcase_t ctype_tests[] = { { "ctype_char", test_ctype_char }, END_OF_TESTCASES }; pgqd/lib/test/connect-tls.c0000664000401600040160000000735713175113172014212 0ustar cbecbe #include #include #include #include #include #include #include #ifdef USUAL_LIBSSL_FOR_TLS #include #endif static void show_time(const char *desc, time_t t) { const char *val; val = t ? ctime(&t) : "--\n"; printf("%s: %s", desc, val); } static void show_ocsp_info(const char *desc, struct tls *ctx) { int req_status, cert_status, crl_reason, res; const char *msg; time_t this_update, next_update, revocation_time; res = tls_get_ocsp_info(ctx, &req_status, &cert_status, &crl_reason, &this_update, &next_update, &revocation_time, &msg); printf("%s: %s\n", desc, msg); if (res == 0) { printf(" req_status=%d cert_status=%d crl_reason=%d\n", req_status, cert_status, crl_reason); show_time(" this update", this_update); show_time(" next update", next_update); show_time(" revocation", revocation_time); } } static void ignore_sigpipe(void) { #ifndef WIN32 static bool done; sigset_t set; int ret; if (done) return; /* block SIGPIPE */ sigemptyset(&set); sigaddset(&set, SIGPIPE); ret = sigprocmask(SIG_BLOCK, &set, NULL); if (ret < 0) err(1, "sigprocmask"); done = true; #endif } static void test_context(struct tls *ctx) { char buf[2*1024*1024], *ptr = buf; ssize_t ret, len = sizeof buf; ignore_sigpipe(); memset(buf, 'X', len); buf[len - 4] = ':'; buf[len - 3] = 'x'; buf[len - 2] = '\r'; buf[len - 1] = '\n'; loop: len = sizeof buf; ptr = buf; while (len > 0) { ret = tls_write(ctx, ptr, len); if (ret <= 0) printf("tls_write(%d) = %d\n", (int)len, (int)ret); if (ret > 0) { len -= ret; ptr += ret; } else if (ret == TLS_WANT_POLLIN) { continue; } else if (ret == TLS_WANT_POLLOUT) { continue; } else { printf("tls_write: %s\n", tls_error(ctx)); break; } } if (len == 0) goto loop; printf("final len: %d\n", (int)len); } int main(int argc, char *argv[]) { struct tls_config *conf; struct tls *ctx, *ocsp; struct tls_cert *cert; int res; const char *host; char buf[256]; if (argc < 2) errx(1, "give host as arg\n"); host = argv[1]; #ifdef USUAL_LIBSSL_FOR_TLS printf("libssl: %s\n", SSLeay_version(SSLEAY_VERSION)); #endif res = tls_init(); if (res < 0) errx(1, "tls_init"); conf = tls_config_new(); if (!conf) errx(1, "tls_config_new"); tls_config_set_protocols(conf, TLS_PROTOCOLS_ALL); tls_config_set_ciphers(conf, "fast"); ctx = tls_client(); if (!ctx) errx(1, "tls_client"); res = tls_configure(ctx, conf); if (res < 0) errx(1, "tls_configure: %s", tls_error(ctx)); res = tls_connect(ctx, host, "443"); if (res < 0) errx(1, "tls_connect: %s", tls_error(ctx)); res = tls_handshake(ctx); if (res < 0) errx(1, "tls_handshake: %s", tls_error(ctx)); res = tls_get_peer_cert(ctx, &cert, NULL); if (res < 0) errx(1, "tls_get_peer_cert: %s", tls_error(ctx)); tls_get_connection_info(ctx, buf, sizeof buf); printf("Connection: '%s'\n", buf); printf(" CN='%s'\n", cert->subject.common_name); printf(" C='%s'\n", cert->subject.country_name); printf(" ST='%s'\n", cert->subject.state_or_province_name); printf(" L='%s'\n", cert->subject.locality_name); printf(" S='%s'\n", cert->subject.street_address); printf(" O='%s'\n", cert->subject.organization_name); printf(" OU='%s'\n", cert->subject.organizational_unit_name); show_ocsp_info("OCSP stapling", ctx); ocsp = NULL; res = tls_ocsp_check_peer(&ocsp, NULL, ctx); if (ocsp) { show_ocsp_info("OCSP responder", ocsp); tls_free(ocsp); } else if (res == TLS_NO_OCSP) { printf("OCSP responder: No OCSP support in libtls\n"); } if (0) test_context(ctx); tls_close(ctx); tls_free(ctx); tls_config_free(conf); tls_cert_free(cert); return 0; } pgqd/lib/test/test_fileutil.c0000664000401600040160000000256113175113172014625 0ustar cbecbe#include #include #include #include "test_common.h" /* * LN1 = 4*8 * LN2 = 8*4*8 * LN3 = 8*8*4*8 */ #define LN1 "11112222333344445555666677778888" #define LN2 LN1 LN1 LN1 LN1 LN1 LN1 LN1 LN1 #define LN3 LN2 LN2 LN2 LN2 LN2 LN2 LN2 LN2 static const char fdata[] = "1\n" "line 2\n" "\n" LN3 "noln"; static const char filename[] = "test_fileutil.tmp"; static bool createfile(void) { FILE *f = fopen(filename, "wb+"); if (!f) return false; fwrite(fdata, 1, strlen(fdata), f); fclose(f); return true; } static void test_fsize(void *p) { int_check(createfile(), 1); tt_assert(file_size(filename) == (int)strlen(fdata)); tt_assert(file_size(filename) == (int)sizeof(fdata) - 1); tt_assert(file_size("nonexist") == -1); end:; } static bool addln(void *arg, const char *ln, ssize_t len) { struct MBuf *buf = arg; int xlen = len; if (len < 0) return false; if (len > 0 && ln[len - 1] == '\n') xlen--; if (memchr(ln, '\n', xlen)) return false; return mbuf_write(buf, ln, len); } static void test_getline(void *p) { struct MBuf buf; mbuf_init_dynamic(&buf); tt_assert(foreach_line(filename, addln, &buf)); tt_assert(mbuf_write_byte(&buf, 0)); end: unlink(filename); mbuf_free(&buf); } struct testcase_t fileutil_tests[] = { { "file_size", test_fsize }, { "getline", test_getline }, END_OF_TESTCASES }; pgqd/lib/test/test_hashtab.c0000664000401600040160000000412213175113172014415 0ustar cbecbe #include #include #include #include "test_common.h" struct MyNode { int value; }; static int cf_size = 64; static int cf_ofs = 0; static int cf_cnt = 3 * 64; static int cf_mod = 13; static bool mycmp(const htab_val_t curval, const void *arg) { const struct MyNode *n1 = curval; const struct MyNode *n2 = arg; return n1->value == n2->value; } static struct MyNode *make_node(int v) { struct MyNode *n = malloc(sizeof(*n)); n->value = v; return n; } /* * checking operations */ static const char *my_insert(struct HashTab *htab, int value) { struct MyNode *my = make_node(value); void **p; int key = value % cf_mod; p = hashtab_lookup(htab, key, true, my); if (!p) return "FAIL"; if (*p) return "EXISTS?"; *p = my; return "OK"; } static const char *my_remove(struct HashTab *h, int value) { struct MyNode tmp, *my; void **p; int key = value % cf_mod; tmp.value = value; p = hashtab_lookup(h, key, false, &tmp); if (!p) return "NEXIST"; my = *p; if (my->value != value) return "WRONG"; hashtab_delete(h, key, &tmp); free(my); p = hashtab_lookup(h, key, false, &tmp); if (p) return "EXISTS?"; return "OK"; } static const char *my_lookup(struct HashTab *htab, int value) { void **p; struct MyNode tmp, *my; int key = value % cf_mod; tmp.value = value; p = hashtab_lookup(htab, key, false, &tmp); if (!p) return "NEXIST"; my = *p; if (my->value != value) return "WRONG"; return "OK"; } /* * Simple operations. */ static void test_hash_basic(void *p) { struct HashTab *htab; int i; htab = hashtab_create(cf_size, mycmp, NULL); for (i = 0; i < cf_cnt; i++) { int n = i + cf_ofs; str_check(my_lookup(htab, n), "NEXIST"); str_check(my_insert(htab, n), "OK"); str_check(my_lookup(htab, n), "OK"); } for (i = 0; i < cf_cnt; i++) { int n = i + cf_ofs; str_check(my_lookup(htab, n), "OK"); str_check(my_remove(htab, n), "OK"); str_check(my_lookup(htab, n), "NEXIST"); } end: hashtab_destroy(htab); } struct testcase_t hashtab_tests[] = { { "basic", test_hash_basic }, END_OF_TESTCASES }; pgqd/lib/test/coverage.mk0000664000401600040160000000043313175113172013725 0ustar cbecbe AM_FEATURES = libusual USUAL_DIR = . noinst_PROGRAMS = covtest covtest_SOURCES := $(wildcard test/test_*.[ch]) test/tinytest.c test/tinytest.h test/tinytest_macros.h covtest_CPPFLAGS = -Itest -I. -DUSUAL_TEST_CONFIG covtest_LDFLAGS = covtest_EMBED_LIBUSUAL = 1 include build.mk pgqd/lib/m4/0000775000401600040160000000000013175113172011142 5ustar cbecbepgqd/lib/m4/usual.m40000664000401600040160000003334313175113172012543 0ustar cbecbe dnl Those depend on correct order: dnl AC_USUAL_INIT dnl AC_USUAL_PROGRAM_CHECK dnl AC_USUAL_HEADER_CHECK dnl AC_USUAL_TYPE_CHECK dnl AC_USUAL_FUNCTION_CHECK dnl Order does not matter: dnl AC_USUAL_CASSERT dnl AC_USUAL_WERROR dnl AC_USUAL_DEBUG dnl Optional features: dnl AC_USUAL_LIBEVENT / AC_USUAL_LIBEVENT_OPT dnl AC_USUAL_UREGEX dnl AC_USUAL_GETADDRINFO_A dnl AC_USUAL_TLS dnl dnl AC_USUAL_INIT: dnl - Sets PORTNAME=win32/unix dnl - If building from separate dir, writes top-level Makefile (antimake) dnl dnl Also defines port-specific flags: dnl _GNU_SOURCE, _WIN32_WINNT, WIN32_LEAN_AND_MEAN dnl AC_DEFUN([AC_USUAL_INIT], [ # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi AC_MSG_CHECKING([target host type]) xhost="$host_alias" if test "x$xhost" = "x"; then xhost=`uname -s` fi case "$xhost" in *cygwin* | *mingw* | *pw32* | *MINGW*) LIBS="$LIBS -lws2_32" PORTNAME=win32;; *) PORTNAME=unix ;; esac AC_SUBST(PORTNAME) AC_MSG_RESULT([$PORTNAME]) dnl Set the flags before any feature tests. if test "$PORTNAME" = "win32"; then AC_DEFINE([WIN32_LEAN_AND_MEAN], [1], [Define to request cleaner win32 headers.]) AC_DEFINE([WINVER], [0x0600], [Define to max win32 API version (0x0600=Vista).]) else AC_DEFINE([_GNU_SOURCE], [1], [Define to get working glibc.]) fi dnl Package-specific data AC_SUBST([pkgdatadir], ['${datarootdir}'/${PACKAGE_TARNAME}]) dnl pkgconfig files AC_SUBST([pkgconfigdir], ['${libdir}/pkgconfig']) ]) dnl Old name for initial checks AC_DEFUN([AC_USUAL_PORT_CHECK], [AC_USUAL_INIT]) dnl dnl AC_USUAL_PROGRAM_CHECK: Simple C environment: CC, CPP, INSTALL dnl AC_DEFUN([AC_USUAL_PROGRAM_CHECK], [ AC_PROG_CC_STDC AC_PROG_CPP dnl Check if compiler supports __func__ AC_CACHE_CHECK([whether compiler supports __func__], pgac_cv_funcname_func, [AC_TRY_COMPILE([#include ], [printf("%s\n", __func__);], [pgac_cv_funcname_func=yes], [pgac_cv_funcname_func=no])]) if test x"$pgac_cv_funcname_func" = xyes ; then AC_DEFINE(HAVE_FUNCNAME__FUNC, 1, [Define to 1 if your compiler understands __func__.]) fi dnl Check if linker supports -Wl,--as-needed if test "$GCC" = "yes"; then old_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--as-needed" AC_MSG_CHECKING([whether linker supports --as-needed]) AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) LDFLAGS="$old_LDFLAGS"]) fi dnl Check if compiler supports gcc-style dependencies AC_MSG_CHECKING([whether compiler supports dependency generation]) old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -MD -MP -MT conftest.o -MF conftest.o.d" AC_COMPILE_IFELSE([AC_LANG_SOURCE([void foo(void){}])], [HAVE_CC_DEPFLAG=yes], [HAVE_CC_DEPFLAG=no]) rm -f conftest.d CFLAGS="$old_CFLAGS" AC_MSG_RESULT([$HAVE_CC_DEPFLAG]) AC_SUBST(HAVE_CC_DEPFLAG) dnl Pick good warning flags for gcc WFLAGS="" if test x"$GCC" = xyes; then AC_MSG_CHECKING([for working warning switches]) good_CFLAGS="$CFLAGS" flags="-Wall -Wextra" # turn off noise from Wextra flags="$flags -Wno-unused-parameter -Wno-missing-field-initializers" # Wextra does not turn those on? flags="$flags -Wmissing-prototypes -Wpointer-arith -Wendif-labels" flags="$flags -Wdeclaration-after-statement -Wold-style-definition" flags="$flags -Wstrict-prototypes -Wundef -Wformat=2" flags="$flags -Wuninitialized" for f in $flags; do CFLAGS="$good_CFLAGS $WFLAGS $f" AC_COMPILE_IFELSE([AC_LANG_SOURCE([void foo(void){}])], [WFLAGS="$WFLAGS $f"]) done # avoid -Wextra if missing-field.initializers does not work echo "$WFLAGS" | grep missing-field-initializers > /dev/null \ || WFLAGS=`echo "$WFLAGS"|sed 's/ -Wextra//'` CFLAGS="$good_CFLAGS" AC_MSG_RESULT([done]) fi AC_SUBST(WFLAGS) AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_EGREP AC_PROG_AWK dnl AC_PROG_MKDIR_P and AC_PROG_SED are from newer autotools m4_ifdef([AC_PROG_MKDIR_P], [ AC_PROG_MKDIR_P ], [ MKDIR_P="mkdir -p" AC_SUBST(MKDIR_P) ]) m4_ifdef([AC_PROG_SED], [ AC_PROG_SED ], [ SED="sed" AC_SUBST(SED) ]) dnl Convert relative path to absolute path. case "$ac_install_sh" in ./*) ac_install_sh="`pwd`/${ac_install_sh}" ;; ../*) ac_install_sh="`pwd`/${ac_install_sh}" ;; esac case "$INSTALL" in ./*) INSTALL="`pwd`/${INSTALL}" ;; ../*) INSTALL="`pwd`/${INSTALL}" ;; esac case "$MKDIR_P" in ./*) MKDIR_P="`pwd`/${MKDIR_P}" ;; ../*) MKDIR_P="`pwd`/${MKDIR_P}" ;; esac AC_CHECK_TOOL([STRIP], [strip]) AC_CHECK_TOOL([RANLIB], [ranlib], [true]) AC_CHECK_TOOL([AR], [ar]) ARFLAGS=rcu AC_SUBST(ARFLAGS) ]) dnl dnl AC_USUAL_TYPE_CHECK: Basic types for C dnl AC_DEFUN([AC_USUAL_TYPE_CHECK], [ AC_C_INLINE AC_C_RESTRICT AC_C_BIGENDIAN AC_SYS_LARGEFILE AC_TYPE_PID_T AC_TYPE_UID_T AC_TYPE_SIZE_T ]) dnl dnl AC_USUAL_HEADER_CHECK: Basic headers dnl AC_DEFUN([AC_USUAL_HEADER_CHECK], [ AC_CHECK_HEADERS([inttypes.h stdbool.h unistd.h sys/time.h]) AC_CHECK_HEADERS([sys/socket.h poll.h sys/poll.h sys/un.h]) AC_CHECK_HEADERS([arpa/inet.h netinet/in.h netinet/tcp.h]) AC_CHECK_HEADERS([sys/param.h sys/uio.h pwd.h grp.h]) AC_CHECK_HEADERS([sys/wait.h sys/mman.h syslog.h netdb.h dlfcn.h]) AC_CHECK_HEADERS([err.h pthread.h endian.h sys/endian.h byteswap.h]) AC_CHECK_HEADERS([malloc.h regex.h getopt.h fnmatch.h]) AC_CHECK_HEADERS([langinfo.h xlocale.h linux/random.h]) dnl ucred.h may have prereqs AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif ]) ]) dnl dnl AC_USUAL_FUNCTION_CHECK: Basic functions dnl AC_DEFUN([AC_USUAL_FUNCTION_CHECK], [ ### Functions provided if missing dnl AC_CHECK_FUNCS(basename dirname) # unstable, provide always AC_CHECK_FUNCS(strlcpy strlcat strnlen strsep getpeereid sigaction sigqueue) AC_CHECK_FUNCS(memmem memrchr mempcpy) AC_CHECK_FUNCS(inet_ntop inet_pton poll getline regcomp) AC_CHECK_FUNCS(err errx warn warnx getprogname setprogname) AC_CHECK_FUNCS(posix_memalign memalign valloc explicit_bzero memset_s reallocarray) AC_CHECK_FUNCS(getopt getopt_long getopt_long_only) AC_CHECK_FUNCS(fls flsl flsll ffs ffsl ffsll) AC_CHECK_FUNCS(fnmatch mbsnrtowcs nl_langinfo strtod_l strtonum) AC_CHECK_FUNCS(asprintf vasprintf timegm) ### Functions provided only on win32 AC_CHECK_FUNCS(localtime_r gettimeofday recvmsg sendmsg usleep getrusage) ### Functions used by libusual itself AC_CHECK_FUNCS(syslog mmap getpeerucred arc4random_buf getentropy getrandom) ### win32: link with ws2_32 AC_SEARCH_LIBS(WSAGetLastError, ws2_32) AC_FUNC_STRERROR_R ### AC_MSG_CHECKING([for integer enc/dec functions]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #include #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_ENDIAN_H #include #endif char p[[]] = "01234567"; int main(void) { be16enc(p, 0); be32enc(p, 1); be64enc(p, 2); le16enc(p, 2); le32enc(p, 3); le64enc(p, 4); return (int)(be16dec(p) + be32dec(p) + be64dec(p)) + (int)(le16dec(p) + le32dec(p) + le64dec(p)); } ])], [ AC_MSG_RESULT([found]) AC_DEFINE([HAVE_ENCDEC_FUNCS], [1], [Define if *enc & *dec functions are available]) ], [AC_MSG_RESULT([not found])]) ]) dnl dnl AC_USUAL_CASSERT: --enable-cassert switch to set macro CASSERT dnl AC_DEFUN([AC_USUAL_CASSERT], [ AC_ARG_ENABLE(cassert, AC_HELP_STRING([--enable-cassert],[turn on assert checking in code])) AC_MSG_CHECKING([whether to enable asserts]) if test "$enable_cassert" = "yes"; then AC_DEFINE(CASSERT, 1, [Define to enable assert checking]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ]) dnl dnl AC_USUAL_WERROR: --enable-werror switch to turn warnings into errors dnl AC_DEFUN([AC_USUAL_WERROR], [ AC_ARG_ENABLE(werror, AC_HELP_STRING([--enable-werror],[add -Werror to CFLAGS])) AC_MSG_CHECKING([whether to fail on warnings]) if test "$enable_werror" = "yes"; then CFLAGS="$CFLAGS -Werror" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ]) dnl dnl AC_USUAL_DEBUG: --disable-debug switch to strip binary dnl AC_DEFUN([AC_USUAL_DEBUG], [ AC_ARG_ENABLE(debug, AC_HELP_STRING([--disable-debug],[strip binary]), [], [enable_debug=yes]) AC_MSG_CHECKING([whether to build debug binary]) if test "$enable_debug" = "yes"; then LDFLAGS="-g $LDFLAGS" BININSTALL="$INSTALL" AC_MSG_RESULT([yes]) else BININSTALL="$INSTALL -s" AC_MSG_RESULT([no]) fi AC_SUBST(enable_debug) ]) dnl dnl AC_USUAL_LIBEVENT: --with-libevent dnl dnl AC_USUAL_LIBEVENT - prefer-yes: dnl default - search for libevent, error if not found dnl --with - search for libevent, error if not found dnl --without - use libusual dnl dnl AC_USUAL_LIBEVENT_OPT - prefer-no: dnl default - use libusual dnl --with - search for libevent, error if not found dnl --without - use libusual dnl AC_DEFUN([AC_USUAL_LIBEVENT_OPT], [AC_USUAL_LIBEVENT(1)]) AC_DEFUN([AC_USUAL_LIBEVENT], [ ifelse([$#], [0], [levent=yes], [levent=no]) AC_MSG_CHECKING([for libevent]) AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=prefix],[Specify where libevent is installed]), [ if test "$withval" = "no"; then levent=no elif test "$withval" = "yes"; then levent=yes else levent=yes CPPFLAGS="$CPPFLAGS -I$withval/include" LDFLAGS="$LDFLAGS -L$withval/lib" fi ], []) if test "$levent" = "no"; then AC_MSG_RESULT([using usual/event]) AC_DEFINE(HAVE_EVENT_LOOPBREAK, 1, [usual/event.h has it.]) AC_DEFINE(HAVE_EVENT_BASE_NEW, 1, [usual/event.h has it.]) have_libevent=no else # libevent AC_DEFINE(HAVE_LIBEVENT, 1, [Use real libevent.]) LIBS="-levent $LIBS" AC_LINK_IFELSE([AC_LANG_SOURCE([ #include #include #include #include int main(void) { struct event ev; event_init(); event_set(&ev, 1, EV_READ, NULL, NULL); /* this checks for 1.2+ */ event_base_free(NULL); } ])], [AC_MSG_RESULT([found])], [AC_MSG_ERROR([not found, cannot proceed])]) AC_CHECK_FUNCS(event_loopbreak event_base_new evdns_base_new) have_libevent=yes fi # libevent AC_SUBST(have_libevent) ]) dnl AC_USUAL_LIBEVENT dnl dnl AC_USUAL_UREGEX: --with-uregex dnl dnl Allow override of system regex dnl AC_DEFUN([AC_USUAL_UREGEX], [ AC_MSG_CHECKING([whether to force internal regex]) uregex=no AC_ARG_WITH(uregex, AC_HELP_STRING([--with-uregex],[Force use of internal regex]), [ if test "$withval" = "yes"; then uregex=yes fi ], []) if test "$uregex" = "yes"; then AC_MSG_RESULT([yes]) AC_DEFINE(USE_INTERNAL_REGEX, 1, [Define to force use of uRegex.]) else AC_MSG_RESULT([no]) fi ]) dnl AC_USUAL_UREGEX dnl dnl AC_USUAL_GETADDRINFO_A - getaddrinfo_a() is required dnl AC_DEFUN([AC_USUAL_GETADDRINFO_A], [ AC_SEARCH_LIBS(getaddrinfo_a, anl) AC_CACHE_CHECK([whether to use native getaddinfo_a], ac_cv_usual_glibc_gaia, [AC_TRY_LINK([ #include #ifdef HAVE_NETDB_H #include #endif ], [ #if __GLIBC_PREREQ(2,9) getaddrinfo_a(0,NULL,0,NULL); #else none or broken #endif ], [ac_cv_usual_glibc_gaia=yes], [ac_cv_usual_glibc_gaia=no])]) if test x"$ac_cv_usual_glibc_gaia" = xyes ; then AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define to 1 if you have the getaddrinfo_a() function.]) else ACX_PTHREAD(, [AC_MSG_RESULT([Threads not available and fallback getaddrinfo_a() non-functional.])]) CC="$PTHREAD_CC" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$LIBS $PTHREAD_LIBS" fi ]) dnl dnl AC_USUAL_TLS: --with-openssl [ / --with-gnutls ? ] dnl dnl AC_USUAL_TLS - prefer-yes: dnl default - search for libssl, error if not found dnl --with-openssl - search for libssl, error if not found dnl --with-openssl=pfx - search for libssl, error if not found, use pfx dnl --without-openssl - no tls dnl AC_DEFUN([AC_USUAL_TLS],[ dnl values: no, libssl, auto tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="" AC_MSG_CHECKING([for OpenSSL]) AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=prefix], [Specify where OpenSSL is installed]), [ if test "$withval" = "no"; then tls_support=no elif test "$withval" = "yes"; then tls_support=libssl TLS_LIBS="-lssl -lcrypto" else tls_support=libssl TLS_CPPFLAGS="-I$withval/include" TLS_LDFLAGS="-L$withval/lib" TLS_LIBS="-lssl -lcrypto" fi ], [ tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="-lssl -lcrypto" ]) dnl check if libssl works if test "$tls_support" = "auto" -o "$tls_support" = "libssl"; then AC_DEFINE(USUAL_LIBSSL_FOR_TLS, 1, [Use libssl for TLS.]) tmp_LIBS="$LIBS" tmp_LDFLAGS="$LDFLAGS" tmp_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$TLS_CPPFLAGS $CPPFLAGS" LDFLAGS="$TLS_LDFLAGS $LDFLAGS" LIBS="$TLS_LIBS $LIBS" AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_method());]])], [ tls_support=yes; AC_MSG_RESULT([found])], [ AC_MSG_ERROR([not found]) ]) dnl check LibreSSL-only APIs AC_CHECK_FUNCS(SSL_CTX_use_certificate_chain_mem SSL_CTX_load_verify_mem asn1_time_parse) CPPFLAGS="$tmp_CPPFLAGS" LDFLAGS="$tmp_LDFLAGS" LIBS="$tmp_LIBS" dnl Pick default root CA file cafile=auto AC_MSG_CHECKING([for root CA certs]) AC_ARG_WITH(root-ca-file, AC_HELP_STRING([--with-root-ca-file=cafile], [Specify where root CA certs are.]), [ if test "$withval" = "no"; then : elif test "$withval" = "yes"; then : else cafile="$withval" fi ]) if test "$cafile" = "auto"; then for cafile in /etc/ssl/certs/ca-certificates.crt /etc/ssl/cert.pem; do if test -f "$cafile"; then break fi done fi AC_DEFINE_UNQUOTED(USUAL_TLS_CA_FILE, ["$cafile"], [Path to root CA certs.]) AC_MSG_RESULT([$cafile]) else AC_MSG_RESULT([no]) fi AC_SUBST(tls_support) AC_SUBST(TLS_CPPFLAGS) AC_SUBST(TLS_LDFLAGS) AC_SUBST(TLS_LIBS) ]) dnl AC_USUAL_TLS pgqd/lib/m4/antimake.m40000664000401600040160000000033613175113172013177 0ustar cbecbe dnl dnl AMK_INIT: Generate initial makefile dnl AC_DEFUN([AMK_INIT], [ # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi ]) pgqd/lib/m4/acx_pthread.m40000664000401600040160000001613113175113172013670 0ustar cbecbeAC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include <pthread.h>], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [acx_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl ACX_PTHREAD pgqd/lib/.gitignore0000664000401600040160000000061413175113172012613 0ustar cbecbeconfigure usual/config.h* config.status config.log config.mak obj test/obj test/test_config.h test/regtest_* test/compile doc/html *.[oas] *.gc[odn][aov] *.la *.pc *.log *.substvars *.debhelper *.diff zconf.* ./install-sh config.guess config.sub ltmain.sh libtool build.mk .objs .libs .deps install-sh tmp tags mk/temos/html mk/temos/output debian/libusual-dev/ debian/libusual0/ debian/files pgqd/lib/.git0000664000401600040160000000003413175113172011403 0ustar cbecbegitdir: ../.git/modules/lib pgqd/lib/configure.ac0000664000401600040160000000127213175113172013112 0ustar cbecbednl Process this file with autoconf to produce a configure script. AC_INIT([libusual], [1.0], [https://libusual.github.com]) AC_CONFIG_SRCDIR(usual/base.h) AC_CONFIG_HEADER(usual/config.h) AC_PREREQ([2.59]) AC_USUAL_INIT AC_USUAL_PROGRAM_CHECK LT_INIT AC_USUAL_HEADER_CHECK AC_USUAL_TYPE_CHECK AC_USUAL_FUNCTION_CHECK AC_USUAL_CASSERT AC_USUAL_WERROR AC_USUAL_DEBUG AC_USUAL_UREGEX AC_USUAL_LIBEVENT(opt) AC_USUAL_GETADDRINFO_A dnl search for common libraries AC_SEARCH_LIBS(clock_gettime, rt) AC_SEARCH_LIBS(getsockname, socket) AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(hstrerror, resolv) AC_USUAL_TLS dnl Output findings AC_OUTPUT([config.mak mk/libusual.pc build.mk]) pgqd/lib/build.mk.in0000664000401600040160000000012113175113172012651 0ustar cbecbe include @abs_top_builddir@/config.mak include $(abs_top_srcdir)/mk/antimake.mk pgqd/lib/README0000664000401600040160000000011213175113172011474 0ustar cbecbe= libusual = Collection of various code useful for writing server code. pgqd/lib/Makefile0000664000401600040160000001311113175113172012257 0ustar cbecbe AM_CPPFLAGS = -I$(builddir) -I$(srcdir) $(TLS_CPPFLAGS) AM_LDFLAGS = $(TLS_LDFLAGS) AM_LIBS = $(TLS_LIBS) # main target lib_LTLIBRARIES = libusual.la # sources that are not always built EXTRA_libusual_la_SOURCES = usual/pgsocket.h usual/pgsocket.c internal_headers = usual/pgutil_kwlookup.h \ usual/tls/tls_compat.h \ usual/tls/tls_internal.h # sources not in tar.gz nodist_EXTRA_libusual_la_SOURCES = usual/config.h # regular source files libusual_la_SOURCES = usual/config.h.in \ usual/aatree.h usual/aatree.c \ usual/base.h usual/base.c usual/base_win32.h \ usual/bits.h \ usual/cbtree.h usual/cbtree.c \ usual/cfparser.h usual/cfparser.c \ usual/config_msvc.h \ usual/crypto/chacha.h usual/crypto/chacha.c \ usual/crypto/csrandom.h usual/crypto/csrandom.c \ usual/crypto/digest.h usual/crypto/digest.c \ usual/crypto/entropy.h usual/crypto/entropy.c \ usual/crypto/hmac.h usual/crypto/hmac.c \ usual/crypto/keccak.h usual/crypto/keccak.c \ usual/crypto/keccak_prng.h usual/crypto/keccak_prng.c \ usual/crypto/md5.h usual/crypto/md5.c \ usual/crypto/sha1.h usual/crypto/sha1.c \ usual/crypto/sha256.h usual/crypto/sha256.c \ usual/crypto/sha512.h usual/crypto/sha512.c \ usual/crypto/sha3.h usual/crypto/sha3.c \ usual/ctype.h \ usual/cxalloc.h usual/cxalloc.c \ usual/cxextra.h usual/cxextra.c \ usual/daemon.h usual/daemon.c \ usual/endian.h \ usual/err.h usual/err.c \ usual/event.h usual/event.c \ usual/fileutil.h usual/fileutil.c \ usual/fnmatch.h usual/fnmatch.c \ usual/getopt.h usual/getopt.c \ usual/hashing/crc32.h usual/hashing/crc32.c \ usual/hashing/lookup3.h usual/hashing/lookup3.c \ usual/hashing/memhash.h usual/hashing/memhash.c \ usual/hashing/siphash.h usual/hashing/siphash.c \ usual/hashing/spooky.h usual/hashing/spooky.c \ usual/hashing/xxhash.h usual/hashing/xxhash.c \ usual/hashtab-impl.h \ usual/heap.h usual/heap.c \ usual/json.h usual/json.c \ usual/list.h usual/list.c \ usual/logging.h usual/logging.c \ usual/mbuf.h usual/mbuf.c \ usual/mdict.h usual/mdict.c \ usual/mempool.h usual/mempool.c \ usual/misc.h \ usual/netdb.h usual/netdb.c \ usual/pgutil.h usual/pgutil.c usual/pgutil_kwlookup.h \ usual/psrandom.h usual/psrandom.c \ usual/pthread.h usual/pthread.c \ usual/regex.h usual/regex.c \ usual/safeio.h usual/safeio.c \ usual/shlist.h \ usual/signal.h usual/signal.c \ usual/slab.h usual/slab.c \ usual/socket.h usual/socket.c usual/socket_ntop.c usual/socket_pton.c usual/socket_win32.h \ usual/statlist.h \ usual/string.h usual/string.c \ usual/strpool.h usual/strpool.c \ usual/talloc.h usual/talloc.c \ usual/time.h usual/time.c \ usual/tls/tls.h usual/tls/tls.c usual/tls/tls_internal.h \ usual/tls/tls_compat.h usual/tls/tls_compat.c usual/tls/tls_peer.c \ usual/tls/tls_client.c usual/tls/tls_config.c usual/tls/tls_ocsp.c \ usual/tls/tls_server.c usual/tls/tls_util.c usual/tls/tls_verify.c \ usual/tls/tls_cert.h usual/tls/tls_cert.c usual/tls/tls_conninfo.c \ usual/utf8.h usual/utf8.c \ usual/wchar.h usual/wchar.c # we want to filter headers, so cannot use usual install method via _HEADERS USUAL_HEADERS = $(filter-out $(internal_headers), \ $(filter %.h,$(libusual_la_SOURCES) $(nodist_EXTRA_libusual_la_SOURCES))) # define aclocal destination aclocaldir = ${datarootdir}/aclocal AM_DESTINATIONS = aclocal # other files dist_pkgdata_SCRIPTS = find_modules.sh dist_aclocal_DATA = m4/usual.m4 m4/antimake.m4 # test program for link-test noinst_PROGRAMS = test/compile test_compile_SOURCES = test/compile.c test_compile_LDADD = libusual.la test_compile_LIBS = $(TLS_LIBS) # extra clean files DISTCLEANFILES = config.log build.mk config.status libtool config.mak MAINTAINERCLEANFILES = build.mk.in configure install-sh ltmain.sh config.sub config.guess # files for .tgz that are not mentioned in sources EXTRA_DIST = $(MAINTAINERCLEANFILES) # we dont build test subdir by default, but want to include in .tgz DIST_SUBDIRS = test # non-recursive subdir EMBED_SUBDIRS = mk # # Launch Antimake # include build.mk # filter headers when installing install-local: @$(MKDIR_P) $(DESTDIR)$(includedir)/usual @$(MKDIR_P) $(DESTDIR)$(includedir)/usual/hashing @$(MKDIR_P) $(DESTDIR)$(includedir)/usual/crypto @$(MKDIR_P) $(DESTDIR)$(includedir)/usual/tls @for hdr in $(USUAL_HEADERS); do \ echo Filtering $$hdr; \ $(SED) -f mk/safe-headers.sed $$hdr \ > $(DESTDIR)$(includedir)/$$hdr; \ done # Give proper error message build.mk: @echo "Please run ./configure first" @exit 1 %.pc: %.pc.in config.status ./config.status --file $@ # run sparse over code sparse: config.mak REAL_CC="$(CC)" \ $(MAKE) clean libusual.a CC="cgcc -Wsparse-all -Wno-transparent-union" # generate api documentation dox: rm -rf doc/html/mk #rm -rf mk/temos/html doxygen doc/Doxyfile $(MAKE) -C mk/temos html cp -rp mk/temos/html doc/html/mk # # rest is for pgutil_kwlookup generation # PG_CONFIG ?= pg_config KWLIST = $(shell $(PG_CONFIG) --includedir-server)/parser/kwlist.h GPERF = gperf -m5 # requires 8.4+ kws: @test -f "$(KWLIST)" || { echo "kwlist.h not found"; exit 1; } grep '^PG_KEYWORD' "$(KWLIST)" \ | grep -v UNRESERVED \ | sed 's/.*"\(.*\)",.*, *\(.*\)[)].*/\1/' \ >> usual/pgutil_kwlookup.gp kwh: $(GPERF) usual/pgutil_kwlookup.g \ | sed '/^#line/d' \ > usual/pgutil_kwlookup.h sizes: all size `find .objs -name '.libs' -prune -o -name '*.o' -print | sort` %.s: %.c $(CC) -S $(DEFS) $(CFLAGS) $(CPPFLAGS) -I. $< -o - | cleanasm > $@ .PHONY: tags tags: ctags $(libusual_la_SOURCES) .PHONY: nodoc nodoc: @for hdr in `find usual -name '*.h'`; do \ grep -q "$$hdr" doc/mainpage.dox || echo "$$hdr" ; \ done deb: debuild -us -uc -b pgqd/lib/COPYRIGHT0000664000401600040160000000151213175113172012114 0ustar cbecbe/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ pgqd/lib/debian/0000775000401600040160000000000013175113172012044 5ustar cbecbepgqd/lib/debian/compat0000664000401600040160000000000213175113172013242 0ustar cbecbe7 pgqd/lib/debian/source/0000775000401600040160000000000013175113172013344 5ustar cbecbepgqd/lib/debian/source/format0000664000401600040160000000001413175113172014552 0ustar cbecbe3.0 (quilt) pgqd/lib/debian/libusual0.symbols0000664000401600040160000002476413175113172015373 0ustar cbecbelibusual.so.0 libusual0 #MINVER# SSL_CTX_load_verify_mem@Base 1.0 SSL_CTX_set_dh_auto@Base 1.0 SSL_CTX_set_ecdh_auto@Base 1.0 SSL_CTX_use_certificate_chain_mem@Base 1.0 _talloc_const_name@Base 1.0 _talloc_format_name@Base 1.0 _talloc_free@Base 1.0 _talloc_free_children@Base 1.0 _talloc_get_type_abort@Base 1.0 _talloc_move@Base 1.0 _talloc_realloc@Base 1.0 _talloc_reference_named@Base 1.0 _talloc_unlink@Base 1.0 aatree_destroy@Base 1.0 aatree_init@Base 1.0 aatree_insert@Base 1.0 aatree_remove@Base 1.0 aatree_search@Base 1.0 aatree_walk@Base 1.0 calc_crc32@Base 1.0 cbtree_create@Base 1.0 cbtree_delete@Base 1.0 cbtree_destroy@Base 1.0 cbtree_insert@Base 1.0 cbtree_lookup@Base 1.0 cbtree_walk@Base 1.0 cf_get@Base 1.0 cf_get_int@Base 1.0 cf_get_lookup@Base 1.0 cf_get_str@Base 1.0 cf_get_time_double@Base 1.0 cf_get_time_usec@Base 1.0 cf_get_uint@Base 1.0 cf_load_file@Base 1.0 cf_logfile@Base 1.0 cf_logfile_level@Base 1.0 cf_quiet@Base 1.0 cf_set@Base 1.0 cf_set_filename@Base 1.0 cf_set_int@Base 1.0 cf_set_lookup@Base 1.0 cf_set_str@Base 1.0 cf_set_time_double@Base 1.0 cf_set_time_usec@Base 1.0 cf_set_uint@Base 1.0 cf_stderr_level@Base 1.0 cf_syslog@Base 1.0 cf_syslog_facility@Base 1.0 cf_syslog_ident@Base 1.0 cf_syslog_level@Base 1.0 cf_verbose@Base 1.0 chacha_keystream@Base 1.0 chacha_keystream_xor@Base 1.0 chacha_set_key_128@Base 1.0 chacha_set_key_256@Base 1.0 chacha_set_nonce@Base 1.0 compat_getpeereid@Base 1.0 csrandom@Base 1.0 csrandom_bytes@Base 1.0 csrandom_range@Base 1.0 cx_alloc0@Base 1.0 cx_alloc@Base 1.0 cx_asprintf@Base 1.0 cx_destroy@Base 1.0 cx_free@Base 1.0 cx_libc_allocator@Base 1.0 cx_libc_nofail@Base 1.0 cx_memdup@Base 1.0 cx_new_pool@Base 1.0 cx_new_pool_from_area@Base 1.0 cx_new_tree@Base 1.0 cx_nofail_ops@Base 1.0 cx_realloc@Base 1.0 cx_sprintf@Base 1.0 cx_strdup@Base 1.0 cx_vasprintf@Base 1.0 cx_vsprintf@Base 1.0 daemonize@Base 1.0 digest_MD5@Base 1.0 digest_SHA1@Base 1.0 digest_SHA224@Base 1.0 digest_SHA256@Base 1.0 digest_SHA384@Base 1.0 digest_SHA3_224@Base 1.0 digest_SHA3_256@Base 1.0 digest_SHA3_384@Base 1.0 digest_SHA3_512@Base 1.0 digest_SHA512@Base 1.0 digest_SHAKE128@Base 1.0 digest_SHAKE256@Base 1.0 digest_block_len@Base 1.0 digest_final@Base 1.0 digest_free@Base 1.0 digest_new@Base 1.0 digest_reset@Base 1.0 digest_result_len@Base 1.0 digest_update@Base 1.0 dtostr_dot@Base 1.0 file_size@Base 1.0 foreach_line@Base 1.0 format_time_ms@Base 1.0 format_time_s@Base 1.0 get_cached_time@Base 1.0 get_time_usec@Base 1.0 getentropy@Base 1.0 getprogname@Base 1.0 hash_lookup3@Base 1.0 heap_create@Base 1.0 heap_destroy@Base 1.0 heap_get_obj@Base 1.0 heap_pop@Base 1.0 heap_push@Base 1.0 heap_remove@Base 1.0 heap_reserve@Base 1.0 heap_size@Base 1.0 heap_top@Base 1.0 hmac_block_len@Base 1.0 hmac_final@Base 1.0 hmac_new@Base 1.0 hmac_reset@Base 1.0 hmac_result_len@Base 1.0 hmac_update@Base 1.0 json_dict_get_bool@Base 1.0 json_dict_get_dict@Base 1.0 json_dict_get_float@Base 1.0 json_dict_get_int@Base 1.0 json_dict_get_list@Base 1.0 json_dict_get_opt_bool@Base 1.0 json_dict_get_opt_dict@Base 1.0 json_dict_get_opt_float@Base 1.0 json_dict_get_opt_int@Base 1.0 json_dict_get_opt_list@Base 1.0 json_dict_get_opt_string@Base 1.0 json_dict_get_string@Base 1.0 json_dict_get_value@Base 1.0 json_dict_is_null@Base 1.0 json_dict_iter@Base 1.0 json_dict_put@Base 1.0 json_dict_put_bool@Base 1.0 json_dict_put_float@Base 1.0 json_dict_put_int@Base 1.0 json_dict_put_null@Base 1.0 json_dict_put_string@Base 1.0 json_free_context@Base 1.0 json_list_append@Base 1.0 json_list_append_bool@Base 1.0 json_list_append_float@Base 1.0 json_list_append_int@Base 1.0 json_list_append_null@Base 1.0 json_list_append_string@Base 1.0 json_list_get_bool@Base 1.0 json_list_get_dict@Base 1.0 json_list_get_float@Base 1.0 json_list_get_int@Base 1.0 json_list_get_list@Base 1.0 json_list_get_string@Base 1.0 json_list_get_value@Base 1.0 json_list_is_null@Base 1.0 json_list_iter@Base 1.0 json_new_bool@Base 1.0 json_new_context@Base 1.0 json_new_dict@Base 1.0 json_new_float@Base 1.0 json_new_int@Base 1.0 json_new_list@Base 1.0 json_new_null@Base 1.0 json_new_string@Base 1.0 json_parse@Base 1.0 json_render@Base 1.0 json_set_options@Base 1.0 json_strerror@Base 1.0 json_value_as_bool@Base 1.0 json_value_as_float@Base 1.0 json_value_as_int@Base 1.0 json_value_as_string@Base 1.0 json_value_size@Base 1.0 json_value_type@Base 1.0 keccak_absorb@Base 1.0 keccak_decrypt@Base 1.0 keccak_encrypt@Base 1.0 keccak_forget@Base 1.0 keccak_init@Base 1.0 keccak_pad@Base 1.0 keccak_prng_add_data@Base 1.0 keccak_prng_extract@Base 1.0 keccak_prng_init@Base 1.0 keccak_rewind@Base 1.0 keccak_squeeze@Base 1.0 keccak_squeeze_xor@Base 1.0 list_sort@Base 1.0 load_file@Base 1.0 log_fatal@Base 1.0 log_generic@Base 1.0 logging_prefix_cb@Base 1.0 map_file@Base 1.0 mbstr_decode@Base 1.0 mbuf_make_room@Base 1.0 md5_final@Base 1.0 md5_reset@Base 1.0 md5_update@Base 1.0 mdict_del_key@Base 1.0 mdict_free@Base 1.0 mdict_get_buf@Base 1.0 mdict_get_str@Base 1.0 mdict_new@Base 1.0 mdict_put_str@Base 1.0 mdict_urldecode@Base 1.0 mdict_urlencode@Base 1.0 mdict_walk@Base 1.0 memcspn@Base 1.0 memhash@Base 1.0 memhash_seed@Base 1.0 memhash_string@Base 1.0 mempbrk@Base 1.0 mempool_alloc@Base 1.0 mempool_destroy@Base 1.0 memspn@Base 1.0 parse_ini_file@Base 1.0 parse_word_list@Base 1.0 pg_is_reserved_word@Base 1.0 pg_keyword_lookup_real@Base 1.0 pg_parse_array@Base 1.0 pg_quote_fqident@Base 1.0 pg_quote_ident@Base 1.0 pg_quote_literal@Base 1.0 reset_logging@Base 1.0 reset_time_cache@Base 1.0 sa2str@Base 1.0 safe_accept@Base 1.0 safe_close@Base 1.0 safe_connect@Base 1.0 safe_read@Base 1.0 safe_recv@Base 1.0 safe_recvmsg@Base 1.0 safe_send@Base 1.0 safe_sendmsg@Base 1.0 safe_write@Base 1.0 setprogname@Base 1.0 sha1_final@Base 1.0 sha1_reset@Base 1.0 sha1_update@Base 1.0 sha224_final@Base 1.0 sha224_reset@Base 1.0 sha224_update@Base 1.0 sha256_final@Base 1.0 sha256_reset@Base 1.0 sha256_update@Base 1.0 sha384_final@Base 1.0 sha384_reset@Base 1.0 sha384_update@Base 1.0 sha3_224_reset@Base 1.0 sha3_256_reset@Base 1.0 sha3_384_reset@Base 1.0 sha3_512_reset@Base 1.0 sha3_final@Base 1.0 sha3_update@Base 1.0 sha512_final@Base 1.0 sha512_reset@Base 1.0 sha512_update@Base 1.0 shake128_reset@Base 1.0 shake256_reset@Base 1.0 shake_extract@Base 1.0 shake_update@Base 1.0 signal_pidfile@Base 1.0 siphash24@Base 1.0 siphash24_secure@Base 1.0 slab_active_count@Base 1.0 slab_alloc@Base 1.0 slab_create@Base 1.0 slab_destroy@Base 1.0 slab_free@Base 1.0 slab_free_count@Base 1.0 slab_stats@Base 1.0 slab_total_count@Base 1.0 socket_set_keepalive@Base 1.0 socket_set_nonblocking@Base 1.0 socket_setup@Base 1.0 spookyhash@Base 1.0 strlist_append@Base 1.0 strlist_append_ref@Base 1.0 strlist_empty@Base 1.0 strlist_foreach@Base 1.0 strlist_free@Base 1.0 strlist_new@Base 1.0 strlist_pop@Base 1.0 strpool_create@Base 1.0 strpool_decref@Base 1.0 strpool_free@Base 1.0 strpool_get@Base 1.0 strpool_incref@Base 1.0 strpool_total@Base 1.0 strtod_dot@Base 1.0 talloc_as_cx@Base 1.0 talloc_asprintf@Base 1.0 talloc_asprintf_append@Base 1.0 talloc_asprintf_append_buffer@Base 1.0 talloc_autofree_context@Base 1.0 talloc_check_name@Base 1.0 talloc_disable_null_tracking@Base 1.0 talloc_enable_leak_report@Base 1.0 talloc_enable_leak_report_full@Base 1.0 talloc_enable_null_tracking@Base 1.0 talloc_enable_null_tracking_no_autofree@Base 1.0 talloc_find_parent_byname@Base 1.0 talloc_from_cx@Base 1.0 talloc_get_name@Base 1.0 talloc_get_size@Base 1.0 talloc_is_parent@Base 1.0 talloc_memdup@Base 1.0 talloc_parent@Base 1.0 talloc_parent_name@Base 1.0 talloc_realloc_fn@Base 1.0 talloc_reference_count@Base 1.0 talloc_reparent@Base 1.0 talloc_report@Base 1.0 talloc_report_depth_cb@Base 1.0 talloc_report_depth_file@Base 1.0 talloc_report_full@Base 1.0 talloc_set_abort_fn@Base 1.0 talloc_set_debug@Base 1.0 talloc_set_destructor@Base 1.0 talloc_set_log_fn@Base 1.0 talloc_set_log_stderr@Base 1.0 talloc_set_memlimit@Base 1.0 talloc_set_name@Base 1.0 talloc_set_name_const@Base 1.0 talloc_show_parents@Base 1.0 talloc_steal@Base 1.0 talloc_strdup@Base 1.0 talloc_strdup_append@Base 1.0 talloc_strdup_append_buffer@Base 1.0 talloc_strndup@Base 1.0 talloc_strndup_append@Base 1.0 talloc_strndup_append_buffer@Base 1.0 talloc_total_blocks@Base 1.0 talloc_total_size@Base 1.0 talloc_vasprintf@Base 1.0 talloc_vasprintf_append@Base 1.0 talloc_vasprintf_append_buffer@Base 1.0 tls_accept_fds@Base 1.0 tls_accept_socket@Base 1.0 tls_cert_free@Base 1.0 tls_check_common_name@Base 1.0 tls_check_servername@Base 1.0 tls_check_subject_altname@Base 1.0 tls_client@Base 1.0 tls_close@Base 1.0 tls_config_clear_keys@Base 1.0 tls_config_free@Base 1.0 tls_config_insecure_noverifycert@Base 1.0 tls_config_insecure_noverifyname@Base 1.0 tls_config_new@Base 1.0 tls_config_parse_protocols@Base 1.0 tls_config_set_ca_file@Base 1.0 tls_config_set_ca_mem@Base 1.0 tls_config_set_ca_path@Base 1.0 tls_config_set_cert_file@Base 1.0 tls_config_set_cert_mem@Base 1.0 tls_config_set_ciphers@Base 1.0 tls_config_set_dheparams@Base 1.0 tls_config_set_ecdhecurve@Base 1.0 tls_config_set_key_file@Base 1.0 tls_config_set_key_mem@Base 1.0 tls_config_set_protocols@Base 1.0 tls_config_set_verify_depth@Base 1.0 tls_config_verify@Base 1.0 tls_configure@Base 1.0 tls_configure_keypair@Base 1.0 tls_configure_server@Base 1.0 tls_configure_ssl@Base 1.0 tls_configure_verify@Base 1.0 tls_connect@Base 1.0 tls_connect_fds@Base 1.0 tls_connect_servername@Base 1.0 tls_connect_socket@Base 1.0 tls_error@Base 1.0 tls_free@Base 1.0 tls_get_connection_info@Base 1.0 tls_get_peer_cert@Base 1.0 tls_get_peer_cert_fingerprint@Base 1.0 tls_host_port@Base 1.0 tls_init@Base 1.0 tls_load_file@Base 1.0 tls_match_name@Base 1.0 tls_new@Base 1.0 tls_read@Base 1.0 tls_reset@Base 1.0 tls_server@Base 1.0 tls_server_conn@Base 1.0 tls_set_error@Base 1.0 tls_ssl_error@Base 1.0 tls_write@Base 1.0 unmap_file@Base 1.0 usual_basename@Base 1.0 usual_dirname@Base 1.0 usual_explicit_bzero@Base 1.0 usual_getpeercreds@Base 1.0 usual_reallocarray@Base 1.0 usual_strerror_r@Base 1.0 usual_strlcat@Base 1.0 usual_strlcpy@Base 1.0 usual_strtonum@Base 1.0 utf8_char_size@Base 1.0 utf8_get_char@Base 1.0 utf8_put_char@Base 1.0 utf8_seq_size@Base 1.0 utf8_validate_seq@Base 1.0 utf8_validate_string@Base 1.0 wctype_wcsn@Base 1.0 xmalloc@Base 1.0 xrealloc@Base 1.0 xstrdup@Base 1.0 xxhash@Base 1.0 pgqd/lib/debian/rules0000775000401600040160000000026013175113172013122 0ustar cbecbe#!/usr/bin/make -f #export DH_VERBOSE=1 #export orig_CFLAGS=1 %: dh $@ override_dh_auto_configure: ./autogen.sh ./configure --prefix=/usr --with-openssl --with-libevent pgqd/lib/debian/control0000664000401600040160000000126713175113172013455 0ustar cbecbeSource: libusual Priority: extra Maintainer: Marko Kreen Build-Depends: build-essential, debhelper (>= 7), libssl-dev (>= 1.0.1), libevent-dev (>= 2.0), doxygen Standards-Version: 3.9.2 Section: libs Homepage: https://libusual.github.io/ Vcs-Browser: https://github.com/libusual/libusual Package: libusual-dev Section: libdevel Architecture: any Depends: ${misc:Depends}, libusual (= ${binary:Version}), make Description: Modern C Environment, development files Static library, header files, and documentation for libusual. Package: libusual0 Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends} Description: Modern C Environment, shared library Shared library pgqd/lib/debian/copyright0000664000401600040160000000134613175113172014003 0ustar cbecbe Copyright (C) 2007-2015 Libusual authors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. pgqd/lib/debian/libusual-dev.install0000664000401600040160000000020413175113172016024 0ustar cbecbeusr/include/usual/*.h usr/include/usual/*/*.h usr/share/aclocal usr/share/libusual usr/lib/pkgconfig usr/lib/lib*.a usr/lib/lib*.so pgqd/lib/debian/libusual0.install0000664000401600040160000000002213175113172015326 0ustar cbecbeusr/lib/lib*.so.* pgqd/lib/debian/docs0000664000401600040160000000000713175113172012714 0ustar cbecbeREADME pgqd/lib/debian/libusual-dev.dirs0000664000401600040160000000002413175113172015317 0ustar cbecbeusr/lib usr/include pgqd/lib/debian/changelog0000664000401600040160000000017013175113172013714 0ustar cbecbelibusual (1.0-1) unstable; urgency=low * v1.0 -- Marko Kreen Fri, 07 Aug 2015 00:05:18 +0300 pgqd/lib/debian/libusual0.dirs0000664000401600040160000000001013175113172014616 0ustar cbecbeusr/lib pgqd/lib/usual/0000775000401600040160000000000013175113172011753 5ustar cbecbepgqd/lib/usual/bits.h0000664000401600040160000001263613175113172013075 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Bit arithmetics. * * - is_power_of_2 * - ffs, ffsl, ffsll * - fls, flsl, flsll * - rol16, rol32, rol64 * - ror16, ror32, ror64 */ #ifndef _USUAL_BITS_H_ #define _USUAL_BITS_H_ #include #include #include /** Checks if integer has only one bit set */ static inline bool is_power_of_2(unsigned int n) { return (n > 0) && !(n & (n - 1)); } /* * Single-eval and type-safe rol/ror */ /** Rotate 16-bit int to left */ static inline uint16_t rol16(uint16_t v, int s) { return (v << s) | (v >> (16 - s)); } /** Rotate 32-bit int to left */ static inline uint32_t rol32(uint32_t v, int s) { return (v << s) | (v >> (32 - s)); } /** Rotate 64-bit int to left */ static inline uint64_t rol64(uint64_t v, int s) { return (v << s) | (v >> (64 - s)); } /** Rotate 16-bit int to right */ static inline uint16_t ror16(uint16_t v, int s) { return rol16(v, 16 - s); } /** Rotate 32-bit int to right */ static inline uint32_t ror32(uint32_t v, int s) { return rol32(v, 32 - s); } /** Rotate 64-bit int to right */ static inline uint64_t ror64(uint64_t v, int s) { return rol64(v, 64 - s); } /* * fls(int) * flsl(long) * flsll(long long) * * find MSB bit set, 1-based ofs, 0 if arg == 0 */ #undef fls #undef flsl #undef flsll #define fls(x) usual_fls(x) #define flsl(x) usual_flsl(x) #define flsll(x) usual_flsll(x) #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_clzll) #define _USUAL_FLS_(sfx, type) \ return (x == 0) ? 0 : ((8*sizeof(type)) - __builtin_clz ## sfx(x)) #else #define _USUAL_FLS_(sfx, type) \ unsigned type u = x; \ unsigned int bit; \ if (x == 0) return 0; \ /* count from smallest bit, assuming small values */ \ for (bit = 1; u > 1; bit++) u >>= 1; \ return bit #endif /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int fls(int x) { _USUAL_FLS_(, int); } /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int flsl(long x) { _USUAL_FLS_(l, long); } /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int flsll(long long x) { _USUAL_FLS_(ll, long long); } #undef _USUAL_FLS_ /* * ffs(int) * ffsl(long) * ffsll(long long) * * find LSB bit set, 1-based ofs, 0 if arg == 0 */ #undef ffs #undef ffsl #undef ffsll #define ffs(x) usual_ffs(x) #define ffsl(x) usual_ffsl(x) #define ffsll(x) usual_ffsll(x) #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_ffsll) #define _USUAL_FFS_(sfx, type) \ return __builtin_ffs ## sfx((unsigned type)(x)) #else #define _USUAL_FFS_(sfx, type) \ unsigned int bit; \ unsigned type u = x; \ if (!x) return 0; \ /* count from smallest bit, assuming small values */ \ for (bit = 1; !(u & 1); bit++) u >>= 1; \ return bit #endif /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffs(int x) { _USUAL_FFS_(, int); } /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffsl(long x) { _USUAL_FFS_(l, long); } /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffsll(long long x) { _USUAL_FFS_(ll, long long); } #undef _USUAL_FFS_ /* * Multiply and check overflow. */ #define _USUAL_MUL_SAFE_(type, max) \ type unsafe = (type)(1) << (sizeof(type) * 8/2); /* sqrt(max+1) */ \ if (a < unsafe && b < unsafe) \ goto safe; \ if (!a || !b) \ goto safe; \ if ((max / a) >= b) \ goto safe; \ return false; \ safe: \ *res_p = a * b; \ return true; /** Multiply with overflow check for 'unsigned int' */ static inline bool safe_mul_uint(unsigned int *res_p, unsigned int a, unsigned int b) { _USUAL_MUL_SAFE_(unsigned int, UINT_MAX); } /** Multiply with overflow check for 'unsigned long' */ static inline bool safe_mul_ulong(unsigned long *res_p, unsigned long a, unsigned long b) { _USUAL_MUL_SAFE_(unsigned long, ULONG_MAX); } /** Multiply with overflow check for 'uint8_t' */ static inline bool safe_mul_uint8(uint8_t *res_p, uint8_t a, uint8_t b) { _USUAL_MUL_SAFE_(uint8_t, UINT8_MAX); } /** Multiply with overflow check for 'uint16_t' */ static inline bool safe_mul_uint16(uint16_t *res_p, uint16_t a, uint16_t b) { _USUAL_MUL_SAFE_(uint16_t, UINT16_MAX); } /** Multiply with overflow check for 'uint32_t' */ static inline bool safe_mul_uint32(uint32_t *res_p, uint32_t a, uint32_t b) { _USUAL_MUL_SAFE_(uint32_t, UINT32_MAX); } /** Multiply with overflow check for 'uint64_t' */ static inline bool safe_mul_uint64(uint64_t *res_p, uint64_t a, uint64_t b) { _USUAL_MUL_SAFE_(uint64_t, UINT64_MAX); } /** Multiply with overflow check for 'size_t' */ static inline bool safe_mul_size(size_t *res_p, size_t a, size_t b) { _USUAL_MUL_SAFE_(size_t, SIZE_MAX); } #undef _USUAL_MUL_SAFE_ #endif pgqd/lib/usual/psrandom.c0000664000401600040160000000745313175113172013753 0ustar cbecbe/* * Pseudo-random bytes. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* Written in 2014 by Sebastiano Vigna (vigna@acm.org) To the extent possible under law, the author has dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See . */ /* This is the fastest generator passing BigCrush without systematic failures, but due to the relatively short period it is acceptable only for applications with a mild amount of parallelism; otherwise, use a xorshift1024* generator. The state must be seeded so that it is not everywhere zero. If you have a nonzero 64-bit seed, we suggest to pass it twice through MurmurHash3's avalanching function. */ static inline uint64_t xorshift128plus_core(uint64_t a, uint64_t b, uint64_t *sb) { b ^= b << 23; b ^= a ^ (b >> 17) ^ (a >> 26); *sb = b; return a + b; } /* * End-user APIs for 128-bit and 1024-bit states. */ /* 128-bit state. Period: 2**128 - 1 */ uint64_t xorshift128plus(uint64_t *s0, uint64_t *s1) { /* swap s0 and s1, calculate new s1 */ uint64_t a = *s1, b = *s0; *s0 = a; return xorshift128plus_core(a, b, s1); } #define XS1K_STATE 16 #define XS1K_MASK (XS1K_STATE - 1) /* 1024-bit state. Period: 2**1024 - 1 */ uint64_t xorshift1024plus(uint64_t state[XS1K_STATE], unsigned int counter) { uint64_t *s0 = &state[counter & XS1K_MASK]; uint64_t *s1 = &state[(counter + 1) & XS1K_MASK]; return xorshift128plus_core(*s0, *s1, s1); } /* * csrandom()-style API on top that. */ static uint64_t ps_state[XS1K_STATE]; static uint32_t ps_init, ps_counter, ps_cache; static void ps_initial_seed(void) { csrandom_bytes(ps_state, sizeof ps_state); ps_init = 1; } void pseudo_random_seed(uint64_t a, uint64_t b) { uint64_t X1 = 123456789, X2 = 987654321; int i; /* xorshift does not like all-zero value */ if (a + X1) a += X1; if (b + X2) b += X2; /* fill all state */ for (i = XS1K_STATE - 1; i >= 0; i--) ps_state[i] = xorshift128plus(&a, &b); ps_init = 1; ps_counter = 0; } uint32_t pseudo_random(void) { uint64_t val; if (!ps_init) ps_initial_seed(); if (ps_init == 2) { ps_init = 1; return ps_cache; } val = xorshift1024plus(ps_state, ps_counter++); ps_cache = val >> 32; ps_init = 2; return val; } void pseudo_random_bytes(void *dst, size_t count) { uint32_t val; uint8_t *p = dst; while (count >= 4) { val = pseudo_random(); le32enc(p, val); count -= 4; p += 4; } if (count > 0) { for (val = pseudo_random(); count > 0; count--) { *p++ = val; val >>= 8; } } } uint32_t pseudo_random_range(uint32_t upper_bound) { uint32_t mod, lim, val; if (upper_bound <= 1) return 0; /* 2**32 % x == (2**32 - x) % x */ mod = -upper_bound % upper_bound; /* wait for value in range [0 .. 2**32-mod) */ lim = -mod; /* loop until good value appears */ while (1) { val = pseudo_random(); if (val < lim || lim == 0) return val % upper_bound; } } pgqd/lib/usual/time.h0000664000401600040160000000513113175113172013062 0ustar cbecbe/* * Theme include for time. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Time-related functionality. */ #ifndef _USUAL_TIME_H_ #define _USUAL_TIME_H_ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef _WIN32 #include #endif #include /** Type to hold microseconds. */ typedef uint64_t usec_t; /** How many microseconds in a second. */ #define USEC ((usec_t)1000000) /** Convert usec timestamp to ISO timestamp with millisecond precision: YYYY-mm-dd hh:mm:ss.SSS */ char *format_time_ms(usec_t time, char *dst, unsigned dstlen); /** Convert usec timestamp to ISO timestamp with second precision: YYYY-mm-dd hh:mm:ss */ char *format_time_s(usec_t time, char *dst, unsigned dstlen); /** Query system time */ usec_t get_time_usec(void); /** Query cached system time */ usec_t get_cached_time(void); /** Forget cached system time, next call will fill it. */ void reset_time_cache(void); #ifdef WIN32 #ifndef HAVE_GETTIMEOFDAY #define gettimeofday(t,z) usual_gettimeofday(t,z) /** Compat: gettimeofday() */ int gettimeofday(struct timeval * tp, void * tzp); #endif #ifndef HAVE_LOCALTIME_R #define localtime_r(t,b) usual_localtime_r(t,b) /** Compat: localtime_r() */ struct tm *localtime_r(const time_t *tp, struct tm *buf); #endif #ifndef HAVE_USLEEP #define usleep(x) usual_usleep(x) /** Compat: usleep() */ static inline void usleep(long usec) { Sleep(usec / 1000); } #endif #ifndef HAVE_GETRUSAGE #define getrusage(w,d) usual_getrusage(w,d) #define RUSAGE_SELF 0 /** Compat: rusage for win32 */ struct rusage { struct timeval ru_utime; struct timeval ru_stime; }; /** Compat: getrusage() for win32 */ int getrusage(int who, struct rusage *dst); #endif #endif #ifndef HAVE_TIMEGM #define timegm(tm) usual_timegm(tm) /** Compat: timegm() */ time_t timegm(struct tm *tm); #endif #endif pgqd/lib/usual/statlist.h0000664000401600040160000001011113175113172013765 0ustar cbecbe/* * Wrapper for list.h that keeps track of number of items. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Circular list that keep track of stats about the list. * * Currenly only count of abjects currently in list * is kept track of. The plan was to track more, * like max, but it was not useful enough. */ #ifndef _USUAL_STATLIST_H_ #define _USUAL_STATLIST_H_ #include /** * Header structure for StatList. */ struct StatList { /** Actual list head */ struct List head; /** Count of objects currently in list */ int cur_count; #ifdef LIST_DEBUG /** List name */ const char *name; #endif }; /** Define and initialize StatList head */ #ifdef LIST_DEBUG #define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0, #var } #else #define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0 } #endif /** Add to the start of the list */ static inline void statlist_prepend(struct StatList *list, struct List *item) { list_prepend(&list->head, item); list->cur_count++; } /** Add to the end of the list */ static inline void statlist_append(struct StatList *list, struct List *item) { list_append(&list->head, item); list->cur_count++; } /** Remove element from the list */ static inline void statlist_remove(struct StatList *list, struct List *item) { list_del(item); list->cur_count--; /* Assert(list->cur_count >= 0); */ } /** Initialize StatList head */ static inline void statlist_init(struct StatList *list, const char *name) { list_init(&list->head); list->cur_count = 0; #ifdef LIST_DEBUG list->name = name; #endif } /** return number of elements currently in list */ static inline int statlist_count(const struct StatList *list) { /* Assert(list->cur_count > 0 || list_empty(&list->head)); */ return list->cur_count; } /** remove and return first element */ static inline struct List *statlist_pop(struct StatList *list) { struct List *item = list_pop(&list->head); if (item) list->cur_count--; /* Assert(list->cur_count >= 0); */ return item; } /** Return first element */ static inline struct List *statlist_first(const struct StatList *list) { return list_first(&list->head); } /** Return last element */ static inline struct List *statlist_last(const struct StatList *list) { return list_last(&list->head); } /** Is list empty */ static inline bool statlist_empty(const struct StatList *list) { return list_empty(&list->head); } /** Loop over list */ #define statlist_for_each(item, list) list_for_each(item, &((list)->head)) /** Loop over list backwards */ #define statlist_for_each_reverse(item, list) list_for_each_reverse(item, &((list)->head)) /** Loop over list safely, so that elements can be removed during */ #define statlist_for_each_safe(item, list, tmp) list_for_each_safe(item, &((list)->head), tmp) /** Loop over list backwards safely, so that elements can be removed during */ #define statlist_for_each_reverse_safe(item, list, tmp) list_for_each_reverse_safe(item, &((list)->head), tmp) /** Put intem before another */ static inline void statlist_put_before(struct StatList *list, struct List *item, struct List *pos) { list_append(pos, item); list->cur_count++; } /** Put item after another */ static inline void statlist_put_after(struct StatList *list, struct List *item, struct List *pos) { list_prepend(pos, item); list->cur_count++; } #endif /* __LIST_H_ */ pgqd/lib/usual/socket.h0000664000401600040160000000761013175113172013420 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Socket compat, few utils. * * Socket headers included: * - win32: * - win32: * - * - * - * - * - * - * - * - */ #ifndef _USUAL_SOCKET_H_ #define _USUAL_SOCKET_H_ #include #ifdef WIN32 #include #include #include #endif #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_POLL_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_SYS_UN_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_TCP_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifndef INADDR_NONE /** Compat: Some systems (Solaris) does not define INADDR_NONE */ #define INADDR_NONE ((unsigned long) -1) #endif /** * Usual socket setup. * * - Disallow SIGPIPE * - Set close-on-exec flag * - Call \ref socket_set_nonblocking() with given flag */ bool socket_setup(int sock, bool non_block); /** * Flip sockets non-blocking flag */ bool socket_set_nonblocking(int sock, bool non_block); /** * Set sockets keepalive flags. * * @param fd TCP socket * @param onoff Whether to set keepalive on or off. * @param keepidle How long the socket must be idle before keepalive packets are sent * @param keepintvl How big period between consecutive keepalive packets. * @param keepcnt How many keepalive packets to send before considering socket dead. */ bool socket_set_keepalive(int fd, int onoff, int keepidle, int keepintvl, int keepcnt); /** * Convert struct sockaddr to stirng. * * Supports: ipv4, ipv5, unix sockets. */ const char *sa2str(const struct sockaddr *sa, char *buf, int buflen); #ifndef HAVE_INET_NTOP #define inet_ntop(a,b,c,d) usual_inet_ntop(a,b,c,d) /** Compat: inet_ntop() */ const char *inet_ntop(int af, const void *src, char *dst, int cnt); #endif #ifndef HAVE_INET_PTON #define inet_pton(a,b,c) usual_inet_pton(a,b,c) /** Compat: inet_pton() */ int inet_pton(int af, const char *src, void *dst); #endif #ifndef HAVE_GETPEEREID #define getpeereid(a,b,c) compat_getpeereid(a,b,c) /** Get user id of UNIX socket peer */ int getpeereid(int fd, uid_t *uid_p, gid_t *gid_p); #endif #define getpeercreds(a,b,c,d) usual_getpeercreds(a,b,c,d) /** Get info of UNIX socket peer */ int getpeercreds(int fd, uid_t *uid_p, gid_t *gid_p, pid_t *pid_p); #if !defined(HAVE_POLL) #define POLLIN (1 << 0) #define POLLOUT (1 << 1) #define POLLHUP (1 << 2) #define POLLPRI (1 << 3) #define POLLNVAL (1 << 4) #define POLLERR (1 << 5) #define poll(a,b,c) compat_poll(a,b,c) struct pollfd { int fd; short events; short revents; }; typedef unsigned long nfds_t; /** Compat: select-based poll() */ int poll(struct pollfd *fds, nfds_t nfds, int timeout_ms); #endif #ifdef WIN32 #define socketpair(a,b,c,d) win32_socketpair(a,b,c,d) /** Compat: socketpair() for win32 */ int socketpair(int d, int typ, int proto, int sv[2]); #endif #endif pgqd/lib/usual/aatree.c0000664000401600040160000001644013175113172013365 0ustar cbecbe/* * AA-Tree - Binary tree with embeddable nodes. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Self-balancing binary tree. * * Here is an implementation of AA-tree (Arne Andersson tree) * which is simplification of Red-Black tree. * * Red-black tree has following properties that must be kept: * 1. A node is either red or black. * 2. The root is black. * 3. All leaves (NIL nodes) are black. * 4. Both childen of red node are black. * 5. Every path from root to leaf contains same number of black nodes. * * AA-tree adds additional property: * 6. Red node can exist only as a right node. * * Red-black tree properties quarantee that the longest path is max 2x longer * than shortest path (B-R-B-R-B-R-B vs. B-B-B-B) thus the tree will be roughly * balanced. Also it has good worst-case guarantees for insertion and deletion, * which makes it good tool for real-time applications. * * AA-tree removes most special cases from RB-tree, thus making resulting * code lot simpler. It requires slightly more rotations when inserting * and deleting but also keeps the tree more balanced. */ #include #include /* for NULL */ typedef struct AATree Tree; typedef struct AANode Node; /* * NIL node */ #define NIL ((struct AANode *)&_nil) static const struct AANode _nil = { NIL, NIL, 0 }; /* * Rebalancing. AA-tree needs only 2 operations * to keep the tree balanced. */ /* * Fix red on left. * * X Y * / --> \ * Y X * \ / * a a */ static inline Node * skew(Node *x) { Node *y = x->left; if (x->level == y->level && x != NIL) { x->left = y->right; y->right = x; return y; } return x; } /* * Fix 2 reds on right. * * X Y * \ / \ * Y --> X Z * / \ \ * a Z a */ static inline Node * split(Node *x) { Node *y = x->right; if (x->level == y->right->level && x != NIL) { x->right = y->left; y->left = x; y->level++; return y; } return x; } /* insert is easy */ static Node *rebalance_on_insert(Node *current) { return split(skew(current)); } /* remove is bit more tricky */ static Node *rebalance_on_remove(Node *current) { /* * Removal can create a gap in levels, * fix it by lowering current->level. */ if (current->left->level < current->level - 1 || current->right->level < current->level - 1) { current->level--; /* if ->right is red, change it's level too */ if (current->right->level > current->level) current->right->level = current->level; /* reshape, ask Arne about those */ current = skew(current); current->right = skew(current->right); current->right->right = skew(current->right->right); current = split(current); current->right = split(current->right); } return current; } /* * Recursive insertion */ static Node * insert_sub(Tree *tree, Node *current, uintptr_t value, Node *node) { int cmp; if (current == NIL) { /* * Init node as late as possible, to avoid corrupting * the tree in case it is already added. */ node->left = node->right = NIL; node->level = 1; tree->count++; return node; } /* recursive insert */ cmp = tree->node_cmp(value, current); if (cmp > 0) current->right = insert_sub(tree, current->right, value, node); else if (cmp < 0) current->left = insert_sub(tree, current->left, value, node); else /* already exists? */ return current; return rebalance_on_insert(current); } void aatree_insert(Tree *tree, uintptr_t value, Node *node) { tree->root = insert_sub(tree, tree->root, value, node); } /* * Recursive removal */ /* remove_sub could be used for that, but want to avoid comparisions */ static Node *steal_leftmost(Tree *tree, Node *current, Node **save_p) { if (current->left == NIL) { *save_p = current; return current->right; } current->left = steal_leftmost(tree, current->left, save_p); return rebalance_on_remove(current); } /* drop this node from tree */ static Node *drop_this_node(Tree *tree, Node *old) { Node *new = NIL; if (old->left == NIL) new = old->right; else if (old->right == NIL) new = old->left; else { /* * Picking nearest from right is better than from left, * due to asymmetry of the AA-tree. It will result in * less tree operations in the long run, */ old->right = steal_leftmost(tree, old->right, &new); /* take old node's place */ *new = *old; } /* cleanup for old node */ if (tree->release_cb) tree->release_cb(old, tree); tree->count--; return new; } static Node *remove_sub(Tree *tree, Node *current, uintptr_t value) { int cmp; /* not found? */ if (current == NIL) return current; cmp = tree->node_cmp(value, current); if (cmp > 0) current->right = remove_sub(tree, current->right, value); else if (cmp < 0) current->left = remove_sub(tree, current->left, value); else current = drop_this_node(tree, current); return rebalance_on_remove(current); } void aatree_remove(Tree *tree, uintptr_t value) { tree->root = remove_sub(tree, tree->root, value); } /* * Walking all nodes */ static void walk_sub(Node *current, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg) { if (current == NIL) return; switch (wtype) { case AA_WALK_IN_ORDER: walk_sub(current->left, wtype, walker, arg); walker(current, arg); walk_sub(current->right, wtype, walker, arg); break; case AA_WALK_POST_ORDER: walk_sub(current->left, wtype, walker, arg); walk_sub(current->right, wtype, walker, arg); walker(current, arg); break; case AA_WALK_PRE_ORDER: walker(current, arg); walk_sub(current->left, wtype, walker, arg); walk_sub(current->right, wtype, walker, arg); break; } } /* walk tree in correct order */ void aatree_walk(Tree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg) { walk_sub(tree->root, wtype, walker, arg); } /* walk tree in bottom-up order, so that walker can destroy the nodes */ void aatree_destroy(Tree *tree) { walk_sub(tree->root, AA_WALK_POST_ORDER, tree->release_cb, tree); /* reset tree */ tree->root = NIL; tree->count = 0; } /* prepare tree */ void aatree_init(Tree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb) { tree->root = NIL; tree->count = 0; tree->node_cmp = cmpfn; tree->release_cb = release_cb; } /* * search function */ Node *aatree_search(Tree *tree, uintptr_t value) { Node *current = tree->root; while (current != NIL) { int cmp = tree->node_cmp(value, current); if (cmp > 0) current = current->right; else if (cmp < 0) current = current->left; else return current; } return NULL; } pgqd/lib/usual/base_win32.h0000664000401600040160000000672413175113172014071 0ustar cbecbe/* * Random win32 compat. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_BASE_WIN32_H_ #define _USUAL_BASE_WIN32_H_ #include #include #include #include #ifndef ECONNABORTED #define ECONNABORTED WSAECONNABORTED #endif #ifndef EMSGSIZE #define EMSGSIZE WSAEMSGSIZE #endif #ifndef EINPROGRESS #define EINPROGRESS WSAEWOULDBLOCK /* WSAEINPROGRESS */ #endif #ifndef SHUT_RDWR #define SHUT_RDWR SD_BOTH #endif #ifndef ENOTCONN #define ENOTCONN WSAENOTCONN #endif #ifndef ECONNRESET #define ECONNRESET WSAECONNRESET #endif #undef EAGAIN #define EAGAIN WSAEWOULDBLOCK /* WSAEAGAIN */ #ifndef EAFNOSUPPORT #define EAFNOSUPPORT ENOSYS #endif #ifndef AI_ADDRCONFIG #define AI_ADDRCONFIG 0 #endif /* dummy types / functions */ #define hstrerror strerror #define getuid() (6667) #define setsid() getpid() #define setgid(x) (-1) #define setuid(x) (-1) #define fork() (-1) #define geteuid() getuid() #define setgroups(s, p) (-1) #define chown(f, u, g) (-1) #define srandom(s) srand(s) #define random() rand() #ifdef _MSC_VER #define snprintf(fmt, ...) _snprintf(fmt, __VA_ARGS__) static inline int strcasecmp(const char *a, const char *b) { return _stricmp(a, b); } static inline int strncasecmp(const char *a, const char *b, size_t cnt) { return _strnicmp(a, b, cnt); } typedef int ssize_t; #endif /* getrlimit() */ #define RLIMIT_NOFILE -1 struct rlimit { int rlim_cur; int rlim_max; }; static inline int getrlimit(int res, struct rlimit *dst) { dst->rlim_cur = dst->rlim_max = -1; return 0; } /* dummy getpwnam() */ struct passwd { char *pw_name; char *pw_passwd; uid_t pw_uid; pid_t pw_gid; char *pw_gecos; char *pw_dir; char *pw_shell; }; static inline struct passwd *getpwnam(const char *u) { return NULL; } static inline struct passwd *getpwuid(uid_t uid) { return NULL; } /* dummy getgrnam() */ struct group { char *gr_name; char *gr_passwd; gid_t gr_gid; char **gr_mem; }; static inline struct group *getgrnam(const char *g) { return NULL; } static inline struct group *getgrgid(gid_t gid) { return NULL; } /* format specifiers that should be in */ #ifndef HAVE_INTTYPES_H #define PRId8 "d" #define PRId16 "d" #define PRId32 "d" #define PRId64 "I64d" #define PRIi8 "d" #define PRIi16 "d" #define PRIi32 "d" #define PRIi64 "I64d" #define PRIo8 "o" #define PRIo16 "o" #define PRIo32 "o" #define PRIo64 "I64o" #define PRIu8 "u" #define PRIu16 "u" #define PRIu32 "u" #define PRIu64 "I64u" #define PRIx8 "x" #define PRIx16 "x" #define PRIx32 "x" #define PRIx64 "I64x" #define PRIX8 "X" #define PRIX16 "X" #define PRIX32 "X" #define PRIX64 "I64X" #endif #define PRIdZ "Id" #define PRIiZ "Ii" #define PRIoZ "Io" #define PRIuZ "Iu" #define PRIxZ "Ix" #define PRIXZ "IX" #endif pgqd/lib/usual/slab.c0000664000401600040160000001471713175113172013052 0ustar cbecbe/* * Primitive slab allocator. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifndef USUAL_FAKE_SLAB /* * Store for pre-initialized objects of one type. */ struct Slab { struct List head; struct StatList freelist; struct StatList fraglist; char name[32]; unsigned final_size; unsigned total_count; slab_init_fn init_func; CxMem *cx; }; /* * Header for each slab. */ struct SlabFrag { struct List head; }; /* keep track of all active slabs */ static STATLIST(slab_list); static void slab_list_append(struct Slab *slab) { #ifndef _REENTRANT statlist_append(&slab_list, &slab->head); #endif } static void slab_list_remove(struct Slab *slab) { #ifndef _REENTRANT statlist_remove(&slab_list, &slab->head); #endif } /* fill struct contents */ static void init_slab(struct Slab *slab, const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { unsigned slen = strlen(name); list_init(&slab->head); statlist_init(&slab->freelist, name); statlist_init(&slab->fraglist, name); slab->total_count = 0; slab->init_func = init_func; slab->cx = cx; if (slen >= sizeof(slab->name)) slen = sizeof(slab->name) - 1; memcpy(slab->name, name, slen); slab->name[slen] = 0; /* don't allow too small align, as we want to put pointers into area */ if (align < sizeof(long)) align = 0; /* actual area for one object */ if (align == 0) slab->final_size = ALIGN(obj_size); else slab->final_size = CUSTOM_ALIGN(obj_size, align); /* allow small structs */ if (slab->final_size < sizeof(struct List)) slab->final_size = sizeof(struct List); slab_list_append(slab); } /* make new slab */ struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { struct Slab *slab; /* new slab object */ slab = cx_alloc0(cx, sizeof(*slab)); if (slab) init_slab(slab, name, obj_size, align, init_func, cx); return slab; } /* free all storage associated by slab */ void slab_destroy(struct Slab *slab) { struct List *item, *tmp; struct SlabFrag *frag; if (!slab) return; slab_list_remove(slab); statlist_for_each_safe(item, &slab->fraglist, tmp) { frag = container_of(item, struct SlabFrag, head); cx_free(slab->cx, frag); } cx_free(slab->cx, slab); } /* add new block of objects to slab */ static void grow(struct Slab *slab) { unsigned count, i, size; char *area; struct SlabFrag *frag; /* calc new slab size */ count = slab->total_count; if (count < 50) count = 16 * 1024 / slab->final_size; if (count < 50) count = 50; size = count * slab->final_size; /* allocate & init */ frag = cx_alloc0(slab->cx, size + sizeof(struct SlabFrag)); if (!frag) return; list_init(&frag->head); area = (char *)frag + sizeof(struct SlabFrag); /* init objects */ for (i = 0; i < count; i++) { void *obj = area + i * slab->final_size; struct List *head = (struct List *)obj; list_init(head); statlist_append(&slab->freelist, head); } /* register to slab */ slab->total_count += count; statlist_append(&slab->fraglist, &frag->head); } /* get free object from slab */ void *slab_alloc(struct Slab *slab) { struct List *item = statlist_pop(&slab->freelist); if (!item) { grow(slab); item = statlist_pop(&slab->freelist); } if (item) { if (slab->init_func) slab->init_func(item); else memset(item, 0, slab->final_size); } return item; } /* put object back to slab */ void slab_free(struct Slab *slab, void *obj) { struct List *item = obj; list_init(item); statlist_prepend(&slab->freelist, item); } /* total number of objects allocated from slab */ int slab_total_count(const struct Slab *slab) { return slab->total_count; } /* free objects in slab */ int slab_free_count(const struct Slab *slab) { return statlist_count(&slab->freelist); } /* number of objects in use */ int slab_active_count(const struct Slab *slab) { return slab_total_count(slab) - slab_free_count(slab); } static void run_slab_stats(struct Slab *slab, slab_stat_fn cb_func, void *cb_arg) { unsigned free = statlist_count(&slab->freelist); cb_func(cb_arg, slab->name, slab->final_size, free, slab->total_count); } /* call a function for all active slabs */ void slab_stats(slab_stat_fn cb_func, void *cb_arg) { struct Slab *slab; struct List *item; statlist_for_each(item, &slab_list) { slab = container_of(item, struct Slab, head); run_slab_stats(slab, cb_func, cb_arg); } } #else struct Slab { int size; struct StatList obj_list; slab_init_fn init_func; CxMem *cx; }; struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { struct Slab *s = cx_alloc(cx, sizeof(*s)); if (s) { s->size = obj_size; s->init_func = init_func; s->cx = cx; statlist_init(&s->obj_list, "obj_list"); } return s; } void slab_destroy(struct Slab *slab) { struct List *el, *tmp; statlist_for_each_safe(el, &slab->obj_list, tmp) { statlist_remove(&slab->obj_list, el); cx_free(slab->cx, el); } cx_free(slab->cx, slab); } void *slab_alloc(struct Slab *slab) { struct List *o; void *res; o = cx_alloc(slab->cx, sizeof(struct List) + slab->size); if (!o) return NULL; list_init(o); statlist_append(&slab->obj_list, o); res = (void *)(o + 1); if (slab->init_func) slab->init_func(res); return res; } void slab_free(struct Slab *slab, void *obj) { if (obj) { struct List *el = obj; statlist_remove(&slab->obj_list, el - 1); cx_free(slab->cx, el - 1); } } int slab_total_count(const struct Slab *slab) { return 0; } int slab_free_count(const struct Slab *slab) { return 0; } int slab_active_count(const struct Slab *slab) { return 0; } void slab_stats(slab_stat_fn cb_func, void *cb_arg) {} #endif pgqd/lib/usual/strpool.c0000664000401600040160000000557313175113172013633 0ustar cbecbe/* * Pool for shared strings. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* * Put all strings into cbtree. */ struct StrPool { CxMem *ca; struct CBTree *tree; int count; }; /* pass key info to cbtree */ static size_t get_key(void *ctx, void *obj, const void **dst_p) { struct PStr *s = obj; *dst_p = s->str; return s->len; } /* free PStr obj */ static bool free_str(void *arg, void *obj) { struct PStr *p = obj; struct StrPool *sp = p->pool; memset(p, 0, offsetof(struct PStr, str) + 1); cx_free(sp->ca, obj); return true; } /* create main structure */ struct StrPool *strpool_create(CxMem *ca) { struct StrPool *sp; sp = cx_alloc(ca, sizeof(*sp)); if (!sp) return NULL; sp->count = 0; sp->ca = ca; sp->tree = cbtree_create(get_key, NULL, NULL, ca); if (!sp->tree) { cx_free(ca, sp); return NULL; } return sp; } /* free main structure */ void strpool_free(struct StrPool *sp) { if (sp) { cbtree_walk(sp->tree, free_str, sp); cbtree_destroy(sp->tree); cx_free(sp->ca, sp); } } /* return total count of strings in pool */ int strpool_total(struct StrPool *sp) { return sp->count; } /* get new reference to str */ struct PStr *strpool_get(struct StrPool *sp, const char *str, ssize_t len) { struct PStr *cstr; bool ok; if (len < 0) len = strlen(str); /* search */ cstr = cbtree_lookup(sp->tree, str, len); if (cstr) { cstr->refcnt++; return cstr; } /* create */ cstr = cx_alloc(sp->ca, sizeof(*cstr) + len + 1); if (!cstr) return NULL; cstr->pool = sp; cstr->refcnt = 1; cstr->len = len; memcpy(cstr->str, str, len + 1); /* insert */ ok = cbtree_insert(sp->tree, cstr); if (!ok) { cx_free(sp->ca, cstr); return NULL; } sp->count++; return cstr; } /* add reference */ void strpool_incref(struct PStr *s) { if (s) s->refcnt++; } /* drop reference, free if none left */ void strpool_decref(struct PStr *s) { struct StrPool *sp; if (!s) return; Assert(s->refcnt > 0); s->refcnt--; if (s->refcnt > 0) return; /* remove */ sp = s->pool; sp->count--; cbtree_delete(sp->tree, s->str, s->len); free_str(NULL, s); } pgqd/lib/usual/event.h0000664000401600040160000001114513175113172013247 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * libevent compat. * * This module adds few functions to older libevent versions, * or provides it's own libevent-compatible event loop * for cases where performance and features of full libevent * are not needed. */ #ifndef _USUAL_EVENT_H_ #define _USUAL_EVENT_H_ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_LIBEVENT /* * Real libevent */ #include #ifndef HAVE_EVENT_BASE_NEW /** Compat: make sure event_base_new() always available */ static inline struct event_base *event_base_new(void) { return event_init(); } #endif #ifndef HAVE_EVENT_LOOPBREAK /** Compat: dummy event_loopbreak for libevent 1.3 */ static inline void event_loopbreak(void) { } #endif #else /* * internal libevent */ #include #include /** * Flags for event_set() / event_assign(): * EV_READ, EV_WRITE, EV_SIGNAL, EV_PERSIST * * Flags given to user callback: * EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT. */ enum EventFlags { EV_TIMEOUT = 1, EV_READ = 2, EV_WRITE = 4, EV_SIGNAL = 8, EV_PERSIST = 16, }; /** Flags for event_loop() */ enum EventLoopType { EVLOOP_ONCE = 1, EVLOOP_NONBLOCK = 2, }; /** Event context. event_base contents are not open */ struct event_base; /** user callback signature */ typedef void (*uevent_cb_f)(int fd, short flags, void *arg); /** Read fd value from struct event */ #define EVENT_FD(ev) ((ev)->fd) /** Read signal value from struct event */ #define EVENT_SIGNAL(ev) ((ev)->fd) /** * Event structure for internal event loop. * * Although the struct is open, no direct accesses should be done. * Thus also the fields are incompatible with libevent. */ struct event { /* node for fd or signal lists */ struct List node; /* timeout info */ usec_t timeout_val; int timeout_idx; /* back-pointer into pollfd list */ int ev_idx; /* event base it is attached to */ struct event_base *base; /* user callback */ uevent_cb_f cb_func; void *cb_arg; /* fd or signal */ int fd; /* both user and internal flags */ short flags; }; struct event_base *event_init(void) _MUSTCHECK; struct event_base *event_base_new(void) _MUSTCHECK; void event_base_free(struct event_base *base); void event_set(struct event *ev, int fd, short flags, uevent_cb_f cb, void *arg); int event_loop(int loop_flags) _MUSTCHECK; int event_loopbreak(void); int event_add(struct event *ev, struct timeval *timeout) _MUSTCHECK; int event_del(struct event *ev); void event_assign(struct event *ev, struct event_base *base, int fd, short flags, uevent_cb_f cb, void *cb_arg); int event_base_loop(struct event_base *base, int loop_flags) _MUSTCHECK; int event_base_loopbreak(struct event_base *base); #define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg) #define evtimer_add(ev, tv) event_add(ev, tv) #define evtimer_del(ev) event_del(ev) #define signal_set(ev, sig, cb, arg) event_set(ev, sig, EV_SIGNAL | EV_PERSIST, cb, arg) #define signal_add(ev, tv) event_add(ev, tv) #define signal_del(ev) event_del(ev) /* random compat */ int event_once(int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout); int event_base_once(struct event_base *base, int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout); int event_loopexit(struct timeval *timeout); int event_base_loopexit(struct event_base *base, struct timeval *timeout); int event_base_set(struct event_base *base, struct event *ev); const char *event_get_version(void); const char *event_get_method(void); /* pointless compat */ #define event_dispatch() event_loop(0) #define event_base_dispatch(base) event_base_loop(base, 0) #define event_initialized(ev) is_event_active(ev) #define signal_initialized(ev) is_event_active(ev) #define evtimer_initialized(ev) is_event_active(ev) int is_event_active(struct event *ev); #endif /* internal libevent */ #endif /* _USUAL_EVENT_H_ */ pgqd/lib/usual/shlist.h0000664000401600040160000001046313175113172013436 0ustar cbecbe/* * Circular list for shared mem. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Circular list for shared mem. * * Instead of pointers, it uses offsets from list head. */ #ifndef _USUAL_SHLIST_H_ #define _USUAL_SHLIST_H_ #include /* clang: pointers are hard */ #if defined(__clang__) #define __shlist_clang_workaround__ volatile #else #define __shlist_clang_workaround__ #endif /** List node/head. Uses offsets from current node instead of direct pointers. */ struct SHList { ptrdiff_t next; ptrdiff_t prev; }; /* * Calculate offset relative to base. * * Instead of using some third pointer (eg. shmem start) as base, * we use node itself as base. This results in simpler APi * and also means that empty node appears as zero-filled. */ /** Get next element in list */ static inline struct SHList *shlist_get_next(const struct SHList *node) { char *p = (char *)node + node->next; return (struct SHList *)p; } /** Get prev element in list */ static inline struct SHList *shlist_get_prev(const struct SHList *node) { char *p = (char *)node + node->prev; return (struct SHList *)p; } static inline void _shlist_set_next(__shlist_clang_workaround__ struct SHList *node, const struct SHList *next) { node->next = (char *)next - (char *)node; } static inline void _shlist_set_prev(__shlist_clang_workaround__ struct SHList *node, const struct SHList *prev) { node->prev = (char *)prev - (char *)node; } /* * List operations. */ /** Initialize list head */ static inline void shlist_init(struct SHList *list) { list->next = 0; list->prev = 0; } /** Insert as last element */ static inline void shlist_append(struct SHList *list, struct SHList *node) { struct SHList *last; last = shlist_get_prev(list); _shlist_set_next(node, list); _shlist_set_prev(node, last); _shlist_set_next(last, node); _shlist_set_prev(list, node); } /** Insert as first element */ static inline void shlist_prepend(struct SHList *list, struct SHList *node) { struct SHList *first; first = shlist_get_next(list); _shlist_set_next(node, first); _shlist_set_prev(node, list); _shlist_set_next(list, node); _shlist_set_prev(first, node); } /** Remove an node */ static inline void shlist_remove(struct SHList *node) { struct SHList *next = shlist_get_next(node); struct SHList *prev = shlist_get_prev(node); _shlist_set_prev(next, prev); _shlist_set_next(prev, next); shlist_init(node); } /** No elements? */ static inline bool shlist_empty(const struct SHList *list) { return list->next == 0; } /** Return first elem */ static inline struct SHList *shlist_first(const struct SHList *list) { if (shlist_empty(list)) return NULL; return shlist_get_next(list); } /** Return last elem */ static inline struct SHList *shlist_last(const struct SHList *list) { if (shlist_empty(list)) return NULL; return shlist_get_prev(list); } /** Remove first elem */ static inline struct SHList *shlist_pop(struct SHList *list) { struct SHList *node = shlist_first(list); if (node) shlist_remove(node); return node; } /** Remove and return specific type of elem */ #define shlist_pop_type(list, type, field) ( \ shlist_empty(list) ? NULL : container_of(shlist_pop(list), type, field)) /** Loop over list */ #define shlist_for_each(node, list) \ for ((node) = shlist_get_next(list); \ (node) != (list); \ (node) = shlist_get_next(node)) /** Loop over list and allow removing node */ #define shlist_for_each_safe(node, list, tmp) \ for ((node) = shlist_get_next(list), (tmp) = shlist_get_next(node); \ (node) != (list); \ (node) = (tmp), (tmp) = shlist_get_next(node)) #endif pgqd/lib/usual/cxextra.c0000664000401600040160000001635613175113172013610 0ustar cbecbe /* * Extra allocators */ #include #include #include #include /* * Tools for allocators. */ static inline void *p_move(const void *p, int ofs) { return (char *)p + ofs; } /* * sample exit-on-failure wrapper */ static void *nofail_alloc(void *next, size_t len) { void *p = cx_alloc(next, len); if (!p) exit(1); return p; } static void *nofail_realloc(void *next, void *ptr, size_t len) { void *p = cx_realloc(next, ptr, len); if (!p) exit(1); return p; } static void nofail_free(void *next, const void *ptr) { cx_free(next, ptr); } static void nofail_destroy(void *next) { cx_destroy(next); } const struct CxOps cx_nofail_ops = { nofail_alloc, nofail_realloc, nofail_free, nofail_destroy, }; const struct CxMem cx_libc_nofail = { &cx_nofail_ops, (void*)&cx_libc_allocator, }; /* * Append-only pool. */ struct CxPoolSeg { struct CxPoolSeg *prev; unsigned char *seg_start; unsigned char *seg_pos; unsigned char *seg_end; }; struct CxPool { struct CxMem this; const struct CxMem *parent; struct CxPoolSeg *last; unsigned char *last_ptr; unsigned int align; bool allow_free_first; struct CxPoolSeg first_seg; }; #define POOL_HDR ALIGN(sizeof(struct CxPoolSeg)) static struct CxPoolSeg *new_seg(struct CxPool *pool, size_t nsize) { struct CxPoolSeg *seg; unsigned char *ptr; size_t alloc = POOL_HDR + nsize; seg = cx_alloc(pool->parent, alloc); if (seg == NULL) return NULL; ptr = (unsigned char *)seg; seg->seg_start = (void *)CUSTOM_ALIGN(ptr + POOL_HDR, pool->align); seg->seg_pos = seg->seg_start; seg->seg_end = (unsigned char *)seg + alloc; seg->prev = pool->last; pool->last = seg; pool->last_ptr = NULL; return seg; } static void *pool_alloc(void *ctx, size_t size) { struct CxPool *pool = ctx; struct CxPoolSeg *seg = pool->last; void *ptr; unsigned nsize; size = CUSTOM_ALIGN(size, pool->align); if (seg && seg->seg_pos + size <= seg->seg_end) { ptr = seg->seg_pos; seg->seg_pos += size; pool->last_ptr = ptr; return ptr; } else { nsize = seg ? (2 * (seg->seg_end - seg->seg_start)) : 512; while (nsize < size) nsize *= 2; seg = new_seg(pool, nsize); if (!seg) return NULL; ptr = seg->seg_pos; seg->seg_pos += size; pool->last_ptr = ptr; return ptr; } } /* free only last item */ static void pool_free(void *ctx, const void *ptr) { struct CxPool *pool = ctx; struct CxPoolSeg *cur = pool->last; if (pool->last_ptr != ptr) return; cur->seg_pos = (void *)ptr; pool->last_ptr = NULL; } static size_t pool_guess_old_len(struct CxPool *pool, unsigned char *ptr) { struct CxPoolSeg *seg = pool->last; unsigned char *cstart; while (seg) { cstart = (void *)CUSTOM_ALIGN((seg + 1), pool->align); if (ptr >= cstart && ptr < seg->seg_pos) return seg->seg_pos - ptr; seg = seg->prev; } return 0; } /* realloc only last item properly, otherwise do new alloc */ static void *pool_realloc(void *ctx, void *ptr, size_t len) { struct CxPool *pool = ctx; struct CxPoolSeg *seg = pool->last; unsigned char *p = ptr; size_t olen; if (pool->last_ptr != ptr) { olen = pool_guess_old_len(pool, ptr); p = pool_alloc(ctx, len); if (!p) return NULL; if (olen > len) olen = len; memcpy(p, ptr, olen); return p; } olen = seg->seg_pos - p; if (seg->seg_pos - olen + len <= seg->seg_end) { seg->seg_pos = p + len; return p; } else { p = pool_alloc(ctx, len); if (!p) return NULL; memcpy(p, ptr, olen); return p; } } static void pool_destroy(void *ctx) { struct CxPool *pool = ctx; struct CxPoolSeg *cur, *prev; if (!pool) return; for (cur = pool->last; cur; ) { prev = cur->prev; if (!prev) break; cx_free(pool->parent, cur); cur = prev; } if (pool->allow_free_first) cx_free(pool->parent, pool); } static const struct CxOps pool_ops = { pool_alloc, pool_realloc, pool_free, pool_destroy, }; /* * public functions */ CxMem *cx_new_pool_from_area(CxMem *parent, void *buf, size_t size, bool allow_free, unsigned int align) { struct CxPool *head; if (size < sizeof(struct CxPool)) return NULL; if (align == 0) align = 8; else if (!is_power_of_2(align)) return NULL; head = buf; memset(head, 0, sizeof(struct CxPool)); head->parent = parent; head->this.ops = &pool_ops; head->this.ctx = head; head->last = &head->first_seg; head->allow_free_first = allow_free; head->align = align; head->first_seg.seg_start = (void *)CUSTOM_ALIGN(head + 1, align); head->first_seg.seg_pos = head->first_seg.seg_start; head->first_seg.seg_end = (unsigned char *)head + size; return &head->this; } CxMem *cx_new_pool(CxMem *parent, size_t initial_size, unsigned int align) { void *area; size_t size; if (initial_size < 1024) initial_size = 1024; size = sizeof(struct CxPool) + initial_size; area = cx_alloc(parent, size); if (!area) return NULL; return cx_new_pool_from_area(parent, area, size, true, align); } /* * tree alloc */ #define TREE_HDR (int)(sizeof(struct CxTreeItem)) struct CxTree { struct CxMem this; CxMem *real; struct List alloc_list; struct List subtree_node; struct List subtree_list; }; /* header for each allocation */ struct CxTreeItem { struct List node; }; static void *tree_alloc(void *ctx, size_t len) { struct CxTree *tree = ctx; struct CxTreeItem *item; item = cx_alloc(tree->real, TREE_HDR + len); if (!item) return NULL; list_init(&item->node); list_append(&tree->alloc_list, &item->node); return p_move(item, TREE_HDR); } static void *tree_realloc(void *ctx, void *ptr, size_t len) { struct CxTree *t = ctx; struct CxTreeItem *item, *item2; item = p_move(ptr, -TREE_HDR); list_del(&item->node); item2 = cx_realloc(t->real, item, TREE_HDR + len); if (item2) { list_append(&t->alloc_list, &item2->node); return p_move(item2, TREE_HDR); } else { list_append(&t->alloc_list, &item->node); return NULL; } } static void tree_free(void *ctx, const void *ptr) { struct CxTree *t = ctx; struct CxTreeItem *item; item = p_move(ptr, -TREE_HDR); list_del(&item->node); cx_free(t->real, item); } static void tree_destroy(void *ctx) { struct CxTree *tree = ctx, *sub; struct CxTreeItem *item; struct List *el, *tmp; /* unregister from parent */ list_del(&tree->subtree_node); /* free elements */ list_for_each_safe(el, &tree->alloc_list, tmp) { list_del(el); item = container_of(el, struct CxTreeItem, node); cx_free(tree->real, item); } /* free subtrees */ list_for_each_safe(el, &tree->subtree_list, tmp) { sub = container_of(el, struct CxTree, subtree_node); tree_destroy(sub); } /* free base struct */ cx_free(tree->real, tree); } static const struct CxOps tree_ops = { tree_alloc, tree_realloc, tree_free, tree_destroy, }; CxMem *cx_new_tree(CxMem *cx) { struct CxTree *t, *parent = NULL; CxMem *real = cx; /* * Try to allocate from real allocator. Otherwise allocations * will have double headers. */ if (cx->ops == &tree_ops) { parent = cx->ctx; real = parent->real; } /* initialize */ t = cx_alloc(real, sizeof(*t)); if (!t) return NULL; t->real = real; t->this.ops = &tree_ops; t->this.ctx = t; list_init(&t->alloc_list); list_init(&t->subtree_node); list_init(&t->subtree_list); /* register at parent */ if (parent) list_append(&parent->subtree_list, &t->subtree_node); return &t->this; } pgqd/lib/usual/pgsocket.h0000664000401600040160000000572413175113172013753 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Async Postgres connection framework. */ #ifndef _USUAL_PGSOCKET_H_ #define _USUAL_PGSOCKET_H_ #include #include /** * Event types reported to user handler function. */ enum PgEvent { /** Connection establishing finished */ PGS_CONNECT_OK, /** Connection establishing failed */ PGS_CONNECT_FAILED, /** Got result from query either resultset or DB error */ PGS_RESULT_OK, /** Query execution failed */ PGS_RESULT_BAD, /** Wakeup from timed sleep */ PGS_TIMEOUT, }; struct PgSocket; struct event_base; typedef void (*pgs_handler_f)(struct PgSocket *pgs, void *arg, enum PgEvent dbev, PGresult *res); /** Create PgSocket. * * It does not launch connection yet, use \ref pgs_connect() for that. * * @param connstr libpq connect string * @param fn callback function for event handling * @param arg extra context for callback * @return Initialized PgSocket structure */ struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *arg); /** Release PgSocket */ void pgs_free(struct PgSocket *db); /** Change the event base for PgSocket */ void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base); /** Set connection lifetime (in seconds) */ void pgs_set_lifetime(struct PgSocket *pgs, double lifetime); /** Launch connection */ void pgs_connect(struct PgSocket *db); /** Drop connection */ void pgs_disconnect(struct PgSocket *db); /** Send simple query */ void pgs_send_query_simple(struct PgSocket *db, const char *query); /** Send extended query, args from varargs */ void pgs_send_query_params(struct PgSocket *db, const char *query, int nargs, ...); /** Send extended query, args from list */ void pgs_send_query_params_list(struct PgSocket *db, const char *query, int nargs, const char *argv[]); /** Ignore the connection for specified time */ void pgs_sleep(struct PgSocket *db, double timeout); /** Disconnect, sleep, reconnect */ void pgs_reconnect(struct PgSocket *db, double timeout); /** Does PgSocket have established connection */ int pgs_connection_valid(struct PgSocket *db); /** Return underlying Postgres connection */ PGconn *pgs_get_connection(struct PgSocket *db); bool pgs_waiting_for_reply(struct PgSocket *db); #endif pgqd/lib/usual/json.h0000664000401600040160000002324013175113172013076 0ustar cbecbe/* * Read and write JSON. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Read and write JSON. * * Features: * - Robust - does not crash or assert() on invalid data. * - Fast memory allocation - it uses single pooled area * for all objects thus alloc/free are fast. * - Strict UTF8 validation. * - Full int64_t and double passthrough, except NaN and +-Infinity. * - Proper number I/O even in weird locales. * * Optional features for JSON config files, off by default: * - Allow C comments. * - Allow extra comma in dict/list. */ #ifndef _USUAL_JSON_ #define _USUAL_JSON_ #include #include /** * JSON value types * * Returned by json_value_type(). */ enum JsonValueType { JSON_NULL = 1, /**< Null value */ JSON_BOOL, /**< Boolean value */ JSON_INT, /**< Integer value */ JSON_FLOAT, /**< Float value */ JSON_STRING, /**< String value */ JSON_LIST, /**< JSON list */ JSON_DICT, /**< JSON "object", which is key->value map */ }; /** Options for JSON parser. */ enum JsonParseOptions { /** Default - do strict parsing. No comments, no extra comma. */ JSON_STRICT = 0, /** Allow comments, allow extra comma. Useful for JSON in config files. */ JSON_PARSE_RELAXED = 1, /** Do not validate UTF-8. The default behavior is to validate UTF-8. */ JSON_PARSE_IGNORE_ENCODING = 2, }; /** * @struct JsonValue * * Json value */ struct JsonValue; /** * @struct JsonContext * * Allocation context. */ struct JsonContext; /** Callback for dict iterator */ typedef bool (*json_dict_iter_callback_f)(void *arg, struct JsonValue *key, struct JsonValue *val); /** Callback for list iterator */ typedef bool (*json_list_iter_callback_f)(void *arg, struct JsonValue *elem); /** * @name Allocation context. * * @{ */ /** Create allocation context */ struct JsonContext *json_new_context(const void *cx_mem, size_t initial_mem); /** Create allocation context */ void json_free_context(struct JsonContext *ctx); /** Create allocation context */ const char *json_strerror(struct JsonContext *ctx); /** * @} * * @name Parse JSON * * @{ */ /** Parse JSON string */ struct JsonValue *json_parse(struct JsonContext *ctx, const char *src, size_t length); /** Set parsing options */ void json_set_options(struct JsonContext *ctx, unsigned int options); /** * @} * * @name Examine single value * * @{ */ /** Return type for value */ enum JsonValueType json_value_type(struct JsonValue *jv); /** * Return element size. * * For JSON strings, it's bytes in string, for list and * dict it returns number of elements. */ size_t json_value_size(struct JsonValue *jv); /** Return true if value is null */ static inline bool json_value_is_null(struct JsonValue *jv) { return json_value_type(jv) == JSON_NULL; } /** Return true if value is boolean */ static inline bool json_value_is_bool(struct JsonValue *jv) { return json_value_type(jv) == JSON_BOOL; } /** Return true if value is int */ static inline bool json_value_is_int(struct JsonValue *jv) { return json_value_type(jv) == JSON_INT; } /** Return true if value is float */ static inline bool json_value_is_float(struct JsonValue *jv) { return json_value_type(jv) == JSON_FLOAT; } /** Return true if value is string */ static inline bool json_value_is_string(struct JsonValue *jv) { return json_value_type(jv) == JSON_STRING; } /** Return true if value is list */ static inline bool json_value_is_list(struct JsonValue *jv) { return json_value_type(jv) == JSON_LIST; } /** Return true if value is dict */ static inline bool json_value_is_dict(struct JsonValue *jv) { return json_value_type(jv) == JSON_DICT; } /** Get bool value */ bool json_value_as_bool(struct JsonValue *jv, bool *dst_p); /** Get int value */ bool json_value_as_int(struct JsonValue *jv, int64_t *dst_p); /** Get double value */ bool json_value_as_float(struct JsonValue *jv, double *dst_p); /** Get string value */ bool json_value_as_string(struct JsonValue *jv, const char **dst_p, size_t *size_p); /** * @} * * @name Get values from dict * * @{ */ /** Get key value from dict */ bool json_dict_get_value(struct JsonValue *dict, const char *key, struct JsonValue **val_p); /** Return true if value is null or missing */ bool json_dict_is_null(struct JsonValue *jv, const char *key); /** Get boolean value from dict */ bool json_dict_get_bool(struct JsonValue *jv, const char *key, bool *dst_p); /** Get int value from dict */ bool json_dict_get_int(struct JsonValue *jv, const char *key, int64_t *dst_p); /** Get float value from dict */ bool json_dict_get_float(struct JsonValue *jv, const char *key, double *dst_p); /** Get string value from dict */ bool json_dict_get_string(struct JsonValue *jv, const char *key, const char **dst_p, size_t *len_p); /** Get sub-dict from dict */ bool json_dict_get_dict(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** Get list from dict */ bool json_dict_get_list(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /* * Optional elements */ /** Get optional bool from dict */ bool json_dict_get_opt_bool(struct JsonValue *jv, const char *key, bool *dst_p); /** Get optional int from dict */ bool json_dict_get_opt_int(struct JsonValue *jv, const char *key, int64_t *dst_p); /** Get optional float from dict */ bool json_dict_get_opt_float(struct JsonValue *jv, const char *key, double *dst_p); /** Get optional string from dict */ bool json_dict_get_opt_string(struct JsonValue *jv, const char *key, const char **dst_p, size_t *len_p); /** Get optional list from dict */ bool json_dict_get_opt_list(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** Get optional dict from dict */ bool json_dict_get_opt_dict(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** * @} * * @name Get values from list. * * @{ */ /** Get value from list */ bool json_list_get_value(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** Return true if value is null or missing */ bool json_list_is_null(struct JsonValue *list, size_t index); /** Get bool from list */ bool json_list_get_bool(struct JsonValue *list, size_t index, bool *val_p); /** Get int from list */ bool json_list_get_int(struct JsonValue *list, size_t index, int64_t *val_p); /** Get float from list */ bool json_list_get_float(struct JsonValue *list, size_t index, double *val_p); /** Get string from list */ bool json_list_get_string(struct JsonValue *list, size_t index, const char **val_p, size_t *len_p); /** Get list value from list */ bool json_list_get_list(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** Get dict value from list */ bool json_list_get_dict(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** * @} * * @name Iterate over elements in list/dict. * * @{ */ /** Walk over dict elements */ bool json_dict_iter(struct JsonValue *dict, json_dict_iter_callback_f cb_func, void *cb_arg); /** Walk over list elements */ bool json_list_iter(struct JsonValue *list, json_list_iter_callback_f cb_func, void *cb_arg); /** * @} * * @name Output JSON. * * @{ */ /** Render JSON object as string */ bool json_render(struct MBuf *dst, struct JsonValue *jv); /** * @} * * @name Create new values. * * @{ */ /** Create NULL value */ struct JsonValue *json_new_null(struct JsonContext *ctx); /** Create bool value */ struct JsonValue *json_new_bool(struct JsonContext *ctx, bool val); /** Create int value */ struct JsonValue *json_new_int(struct JsonContext *ctx, int64_t val); /** Create float value */ struct JsonValue *json_new_float(struct JsonContext *ctx, double val); /** Create string value */ struct JsonValue *json_new_string(struct JsonContext *ctx, const char *val); /** Create dict value */ struct JsonValue *json_new_dict(struct JsonContext *ctx); /** Create list value */ struct JsonValue *json_new_list(struct JsonContext *ctx); /** * @} * * @name Add values to containers. * * @{ */ /** Add value to list */ bool json_list_append(struct JsonValue *list, struct JsonValue *elem); /** Add null to list */ bool json_list_append_null(struct JsonValue *list); /** Add bool to list */ bool json_list_append_bool(struct JsonValue *list, bool val); /** Add int to list */ bool json_list_append_int(struct JsonValue *list, int64_t val); /** Add float to list */ bool json_list_append_float(struct JsonValue *list, double val); /** Add string to list */ bool json_list_append_string(struct JsonValue *list, const char *val); /** Add element to dict */ bool json_dict_put(struct JsonValue *dict, const char *key, struct JsonValue *val); /** Add null to dict */ bool json_dict_put_null(struct JsonValue *dict, const char *key); /** Add bool to dict */ bool json_dict_put_bool(struct JsonValue *dict, const char *key, bool val); /** Add int to dict */ bool json_dict_put_int(struct JsonValue *dict, const char *key, int64_t val); /** Add float to dict */ bool json_dict_put_float(struct JsonValue *dict, const char *key, double val); /** Add string to dict */ bool json_dict_put_string(struct JsonValue *dict, const char *key, const char *str); /** * @} */ #endif pgqd/lib/usual/mempool.h0000664000401600040160000000217113175113172013575 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Simple memory pool for variable-length allocations. */ #ifndef _USUAL_MEMPOOL_H_ #define _USUAL_MEMPOOL_H_ #include /** Pool Reference */ struct MemPool; /** Allocate from pool */ void *mempool_alloc(struct MemPool **pool, unsigned size) _MALLOC; /** Release all memory in pool */ void mempool_destroy(struct MemPool **pool); #endif pgqd/lib/usual/logging.c0000664000401600040160000001457213175113172013556 0ustar cbecbe/* * Logging for unix service. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #ifdef HAVE_SYSLOG_H #include #endif #ifdef WIN32 #define LOG_EMERG 0 #define LOG_ALERT 1 #define LOG_CRIT 2 #define LOG_ERR 3 #define LOG_WARNING 4 #define LOG_NOTICE 5 #define LOG_INFO 6 #define LOG_DEBUG 7 #define LOG_PID 0 #define LOG_DAEMON 0 static inline void openlog(const char *ident, int opt, int fac) {} #define syslog win32_eventlog #define closelog() static void win32_eventlog(int level, const char *fmt, ...); #endif int cf_quiet = 0; int cf_verbose = 0; const char *cf_logfile = NULL; int cf_syslog = 0; const char *cf_syslog_ident = NULL; const char *cf_syslog_facility = NULL; enum LogLevel cf_syslog_level = LG_INFO; enum LogLevel cf_logfile_level = LG_NOISE; enum LogLevel cf_stderr_level = LG_NOISE; /* optional function to fill prefix */ logging_prefix_fn_t logging_prefix_cb; static FILE *log_file = NULL; static bool syslog_started = false; struct LevelInfo { const char *tag; int syslog_prio; }; static const struct LevelInfo log_level_list[] = { { "FATAL", LOG_CRIT }, /* LG_FATAL */ { "ERROR", LOG_ERR }, /* LG_ERROR */ { "WARNING", LOG_WARNING },/* LG_WARNING */ { "LOG", LOG_INFO }, /* LG_STATS*/ { "LOG", LOG_INFO }, /* LG_INFO */ { "DEBUG", LOG_DEBUG }, /* LG_DEBUG */ { "NOISE", LOG_DEBUG }, /* LG_NOISE */ }; struct FacName { const char *name; int code; }; static const struct FacName facility_names [] = { #ifndef WIN32 { "auth", LOG_AUTH }, #ifdef LOG_AUTHPRIV { "authpriv", LOG_AUTHPRIV }, #endif { "daemon", LOG_DAEMON }, { "user", LOG_USER }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, #endif { NULL }, }; void reset_logging(void) { if (log_file) { fclose(log_file); log_file = NULL; } if (syslog_started) { closelog(); syslog_started = 0; } } static void start_syslog(void) { const struct FacName *f; int fac = LOG_DAEMON; const char *ident = cf_syslog_ident; if (!cf_syslog) return; if (cf_syslog_facility) { for (f = facility_names; f->name; f++) { if (strcmp(f->name, cf_syslog_facility) == 0) { fac = f->code; break; } } } if (!ident) { ident = getprogname(); if (!ident) ident = "unnamed"; } openlog(ident, LOG_PID, fac); syslog_started = 1; } void log_generic(enum LogLevel level, void *ctx, const char *fmt, ...) { char buf[2048], buf2[2048]; char ebuf[256]; char timebuf[64]; const struct LevelInfo *lev = &log_level_list[level]; unsigned pid = getpid(); va_list ap; int pfxlen = 0; int old_errno = errno; char *msg = buf; if (logging_prefix_cb) { pfxlen = logging_prefix_cb(level, ctx, buf, sizeof(buf)); if (pfxlen < 0) goto done; if (pfxlen >= (int)sizeof(buf)) pfxlen = sizeof(buf) - 1; } va_start(ap, fmt); vsnprintf(buf + pfxlen, sizeof(buf) - pfxlen, fmt, ap); va_end(ap); /* replace '\n' in message with '\n\t', strip trailing whitespace */ if (strchr(msg, '\n')) { char *dst = buf2; for (; *msg && dst - buf < (int)sizeof(buf2) - 2; msg++) { *dst++ = *msg; if (*msg == '\n') *dst++ = '\t'; } while (dst > buf2 && isspace(dst[-1])) dst--; *dst = 0; msg = buf2; } format_time_ms(0, timebuf, sizeof(timebuf)); if (!log_file && cf_logfile && cf_logfile[0]) { log_file = fopen(cf_logfile, "a"); if (log_file) { /* Got the file, disable buffering */ setvbuf(log_file, NULL, _IONBF, 0); } else { /* Unable to open, complain and fail */ fprintf(stderr, "%s %u %s Cannot open logfile: '%s': %s\n", timebuf, pid, log_level_list[0].tag, cf_logfile, strerror_r(errno, ebuf, sizeof(ebuf))); exit(1); } } if (!cf_quiet && level <= cf_stderr_level) fprintf(stderr, "%s %u %s %s\n", timebuf, pid, lev->tag, msg); if (log_file && level <= cf_logfile_level) fprintf(log_file, "%s %u %s %s\n", timebuf, pid, lev->tag, msg); if (cf_syslog && level <= cf_syslog_level) { if (!syslog_started) start_syslog(); syslog(lev->syslog_prio, "%s", msg); } done: if (old_errno != errno) errno = old_errno; } void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *fmt, ...) { char buf[2048], ebuf[256]; const char *estr = NULL; int old_errno = 0; va_list ap; if (show_perror) { old_errno = errno; estr = strerror_r(errno, ebuf, sizeof(ebuf)); } va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (show_perror) { log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s: %s [%d]", file, line, func, buf, estr, old_errno); } else { log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s", file, line, func, buf); } } #ifdef WIN32 static void win32_eventlog(int level, const char *fmt, ...) { static HANDLE evtHandle = INVALID_HANDLE_VALUE; int elevel; char buf[1024]; const char *strlist[1] = { buf }; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); switch (level) { case LOG_CRIT: case LOG_ERR: elevel = EVENTLOG_ERROR_TYPE; break; case LOG_WARNING: elevel = EVENTLOG_WARNING_TYPE; break; default: elevel = EVENTLOG_INFORMATION_TYPE; } if (evtHandle == INVALID_HANDLE_VALUE) { evtHandle = RegisterEventSource(NULL, cf_syslog_ident); if (evtHandle == NULL || evtHandle == INVALID_HANDLE_VALUE) { evtHandle = INVALID_HANDLE_VALUE; return; } } ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, strlist, NULL); } #endif pgqd/lib/usual/mdict.c0000664000401600040160000001424013175113172013220 0ustar cbecbe/* * A string to string dictionary. */ #include #include #include #include #include struct MDict { struct CBTree *tree; CxMem *cx; }; struct MDictElem { struct MBuf key; struct MBuf val; }; /* hook for CBTree */ static size_t mdict_getkey(void *ctx, void *obj, const void **dst_p) { struct MDictElem *el = obj; *dst_p = mbuf_data(&el->key); return mbuf_written(&el->key); } static bool mdict_free_obj(void *ctx, void *obj) { struct MDictElem *el = obj; struct MDict *dict = ctx; cx_free(dict->cx, mbuf_data(&el->key)); cx_free(dict->cx, mbuf_data(&el->val)); cx_free(dict->cx, el); return true; } struct MDict *mdict_new(CxMem *cx) { struct MDict *dict; dict = cx_alloc(cx, sizeof(struct MDict)); if (!dict) return NULL; dict->cx = cx; dict->tree = cbtree_create(mdict_getkey, mdict_free_obj, dict, cx); if (!dict->tree) { cx_free(cx, dict); return NULL; } return dict; } void mdict_free(struct MDict *dict) { if (dict) { cbtree_destroy(dict->tree); cx_free(dict->cx, dict); } } const struct MBuf *mdict_get_buf(struct MDict *dict, const char *key, unsigned klen) { struct MDictElem *el = cbtree_lookup(dict->tree, key, klen); if (!el) return NULL; return &el->val; } const char *mdict_get_str(struct MDict *dict, const char *key, unsigned klen) { const struct MBuf *val = mdict_get_buf(dict, key, klen); return val ? mbuf_data(val) : NULL; } bool mdict_put_str(struct MDict *dict, const char *key, unsigned klen, const char *val, unsigned vlen) { char *kptr, *vptr = NULL; struct MDictElem *el; if (val) { vptr = cx_alloc(dict->cx, vlen + 1); if (!vptr) return false; memcpy(vptr, val, vlen); vptr[vlen] = 0; } el = cbtree_lookup(dict->tree, key, klen); if (el) { cx_free(dict->cx, mbuf_data(&el->val)); mbuf_init_fixed_reader(&el->val, vptr, vlen); } else { kptr = cx_alloc(dict->cx, klen + 1); if (!kptr) return false; memcpy(kptr, key, klen); kptr[klen] = 0; el = cx_alloc(dict->cx, sizeof(*el)); if (!el) return false; mbuf_init_fixed_reader(&el->key, kptr, klen); mbuf_init_fixed_reader(&el->val, vptr, vlen); if (!cbtree_insert(dict->tree, el)) return false; } return true; } bool mdict_del_key(struct MDict *dict, const char *key, unsigned klen) { return cbtree_delete(dict->tree, key, klen); } /* * walk over key-val pairs */ struct WalkerCtx { mdict_walker_f cb_func; void *cb_arg; }; static bool walk_helper(void *arg, void *elem) { struct WalkerCtx *ctx = arg; struct MDictElem *el = elem; return ctx->cb_func(ctx->cb_arg, &el->key, &el->val); } bool mdict_walk(struct MDict *dict, mdict_walker_f cb_func, void *cb_arg) { struct WalkerCtx ctx; ctx.cb_func = cb_func; ctx.cb_arg = cb_arg; return cbtree_walk(dict->tree, walk_helper, &ctx); } /* * urldecode */ static int gethex(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static void *urldec_str(CxMem *cx, const char **src_p, const char *end, unsigned *len_p) { const char *s; char *d, *dst; int c, len = 0; /* estimate size */ for (s = *src_p; s < end; s++) { if (*s == '%') s += 2; else if (*s == '&' || *s == '=') break; len++; } /* allocate room */ d = dst = cx_alloc(cx, len + 1); if (!dst) return NULL; /* write out */ for (s = *src_p; s < end; ) { if (*s == '%') { if (s + 3 > end) goto err; c = gethex(s[1]) << 4; c |= gethex(s[2]); if (c < 0) goto err; s += 3; *d++ = c; } else if (*s == '+') { *d++ = ' '; s++; } else if (*s == '&' || *s == '=') { break; } else { *d++ = *s++; } } *d = 0; *len_p = d - dst; *src_p = s; return dst; err: cx_free(cx, dst); return NULL; } bool mdict_urldecode(struct MDict *dict, const char *str, unsigned len) { const char *s = str; const char *end = s + len; const char *k, *v; unsigned klen, vlen; struct MDictElem *el; while (s < end) { v = NULL; vlen = 0; el = NULL; /* read key */ k = urldec_str(dict->cx, &s, end, &klen); if (!k) goto fail; /* read value */ if (s < end && *s == '=') { s++; v = urldec_str(dict->cx, &s, end, &vlen); if (!v) goto fail; } if (s < end && *s == '&') s++; /* insert value */ el = cbtree_lookup(dict->tree, k, klen); if (el) { cx_free(dict->cx, mbuf_data(&el->val)); mbuf_init_fixed_reader(&el->val, v, vlen); } else { el = cx_alloc(dict->cx, sizeof(*el)); if (!el) goto fail; mbuf_init_fixed_reader(&el->key, k, klen); mbuf_init_fixed_reader(&el->val, v, vlen); if (!cbtree_insert(dict->tree, el)) goto fail; } } return true; fail: if (k) cx_free(dict->cx, k); if (v) cx_free(dict->cx, v); if (el) cx_free(dict->cx, el); return false; } /* * urlencode */ struct UrlEncCtx { struct MBuf *dst; bool is_first; }; static bool urlenc_str(struct MBuf *dst, const struct MBuf *str) { static const char hextbl[] = "0123456789abcdef"; unsigned len = mbuf_written(str); const unsigned char *s = mbuf_data(str); const unsigned char *end = s + len; bool ok; for (; s < end; s++) { if (*s == ' ') { ok = mbuf_write_byte(dst, '+'); } else if ((*s < 128) && isalnum(*s)) { ok = mbuf_write_byte(dst, *s); } else if (*s == '.' || *s == '_') { ok = mbuf_write_byte(dst, *s); } else { ok = mbuf_write_byte(dst, '%'); ok = ok && mbuf_write_byte(dst, hextbl[*s >> 4]); ok = ok && mbuf_write_byte(dst, hextbl[*s & 15]); } if (!ok) return false; } return true; } static bool urlenc_elem(void *arg, const struct MBuf *key, const struct MBuf *val) { struct UrlEncCtx *ctx = arg; bool ok; if (ctx->is_first) { ctx->is_first = false; } else { ok = mbuf_write_byte(ctx->dst, '&'); if (!ok) return false; } ok = urlenc_str(ctx->dst, key); if (!ok) return false; if (mbuf_data(val) != NULL) { ok = mbuf_write_byte(ctx->dst, '='); if (!ok) return false; ok = urlenc_str(ctx->dst, val); if (!ok) return false; } return true; } bool mdict_urlencode(struct MDict *dict, struct MBuf *dst) { struct UrlEncCtx ctx; ctx.is_first = true; ctx.dst = dst; return mdict_walk(dict, urlenc_elem, &ctx); } pgqd/lib/usual/endian.h0000664000401600040160000002133213175113172013363 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Endianess conversion, convert integers to bytes. */ #ifndef _USUAL_ENDIAN_H_ #define _USUAL_ENDIAN_H_ #include #include /* * Need to include OS headers even if unused, so our * definitions stay in use. */ #ifdef HAVE_ENDIAN_H #include #endif #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_BYTESWAP_H #include #endif /* * Is unaligned access to integers OK? Does not apply to floats. * * OK: x86, amd64, arm >= v6, ppc */ #if defined(__amd64__) || defined(__i386__) || defined(__ppc__) || defined(__ppc64__) \ || defined(__ARM_FEATURE_UNALIGNED) \ || defined(_M_IX86) || defined(_M_X64) || defined(_M_PPC) \ || (defined(_M_ARM) && _M_ARM >= 6) #define WORDS_UNALIGNED_ACCESS_OK #endif /* * Ignore OS defines, as they may define only some subset of functions. * * Instead try to use compiler builtins. */ #undef bswap16 #undef bswap32 #undef bswap64 #undef htobe16 #undef htobe32 #undef htobe64 #undef htole16 #undef htole32 #undef htole64 #undef be16toh #undef be32toh #undef be64toh #undef le16toh #undef le32toh #undef le64toh #undef be16dec #undef be32dec #undef be64dec #undef le16dec #undef le32dec #undef le64dec #undef h16dec #undef h32dec #undef h64dec #undef be16enc #undef be32enc #undef be64enc #undef le16enc #undef le32enc #undef le64enc #undef h16enc #undef h32enc #undef h64enc /* * Redefine to avoid conflicts. */ #define bswap16(x) usual_bswap16(x) #define bswap32(x) usual_bswap32(x) #define bswap64(x) usual_bswap64(x) #define be16dec(p) usual_be16dec(p) #define be32dec(p) usual_be32dec(p) #define be64dec(p) usual_be64dec(p) #define le16dec(p) usual_le16dec(p) #define le32dec(p) usual_le32dec(p) #define le64dec(p) usual_le64dec(p) #define h16dec(p) usual_h16dec(p) #define h32dec(p) usual_h32dec(p) #define h64dec(p) usual_h64dec(p) #define be16enc(p, x) usual_be16enc(p, x) #define be32enc(p, x) usual_be32enc(p, x) #define be64enc(p, x) usual_be64enc(p, x) #define le16enc(p, x) usual_le16enc(p, x) #define le32enc(p, x) usual_le32enc(p, x) #define le64enc(p, x) usual_le64enc(p, x) #define h16enc(p, x) usual_h16enc(p, x) #define h32enc(p, x) usual_h32enc(p, x) #define h64enc(p, x) usual_h64enc(p, x) /** * @name Always swap. * * @{ */ /** Swap 16-bit int */ static inline uint16_t bswap16(uint16_t x) { #if _COMPILER_GNUC(4, 8) || __has_builtin(__builtin_bswap16) return __builtin_bswap16(x); #else return (x << 8) | (x >> 8); #endif } /** Swap 32-bit int */ static inline uint32_t bswap32(uint32_t x) { #if _COMPILER_GNUC(4, 3) || __has_builtin(__builtin_bswap32) return __builtin_bswap32(x); #else x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF); return (x << 16) | (x >> 16); #endif } /** Swap 64-bit int */ static inline uint64_t bswap64(uint64_t x) { #if _COMPILER_GNUC(4, 3) || __has_builtin(__builtin_bswap64) return __builtin_bswap64(x); #else return ((uint64_t)bswap32(x) << 32) | bswap32(x >> 32); #endif } /** * @} * * @name Convert host-endian int to BE/LE. * * @{ */ #ifdef WORDS_BIGENDIAN /** Convert native 16-bit int to big-endian */ #define htobe16(x) ((uint16_t)(x)) /** Convert native 32-bit int to big-endian */ #define htobe32(x) ((uint32_t)(x)) /** Convert native 64-bit int to big-endian */ #define htobe64(x) ((uint64_t)(x)) /** Convert native 16-bit int to little-endian */ #define htole16(x) bswap16(x) /** Convert native 32-bit int to little-endian */ #define htole32(x) bswap32(x) /** Convert native 64-bit int to little-endian */ #define htole64(x) bswap64(x) /** Convert big-endian 16-bit int to host-endian */ #define be16toh(x) ((uint16_t)(x)) /** Convert big-endian 32-bit int to host-endian */ #define be32toh(x) ((uint32_t)(x)) /** Convert big-endian 64-bit int to host-endian */ #define be64toh(x) ((uint64_t)(x)) /** Convert little-endian 16-bit int to host-endian */ #define le16toh(x) bswap16(x) /** Convert little-endian 32-bit int to host-endian */ #define le32toh(x) bswap32(x) /** Convert little-endian 64-bit int to host-endian */ #define le64toh(x) bswap64(x) #else /* !WORDS_BIGENDIAN */ /** Convert native 16-bit int to big-endian */ #define htobe16(x) bswap16(x) /** Convert native 32-bit int to big-endian */ #define htobe32(x) bswap32(x) /** Convert native 64-bit int to big-endian */ #define htobe64(x) bswap64(x) /** Convert native 16-bit int to little-endian */ #define htole16(x) ((uint16_t)(x)) /** Convert native 32-bit int to little-endian */ #define htole32(x) ((uint32_t)(x)) /** Convert native 64-bit int to little-endian */ #define htole64(x) ((uint64_t)(x)) /** Convert big-endian 16-bit int to host-endian */ #define be16toh(x) bswap16(x) /** Convert big-endian 32-bit int to host-endian */ #define be32toh(x) bswap32(x) /** Convert big-endian 64-bit int to host-endian */ #define be64toh(x) bswap64(x) /** Convert little-endian 64-bit int to host-endian */ #define le16toh(x) ((uint16_t)(x)) /** Convert little-endian 64-bit int to host-endian */ #define le32toh(x) ((uint32_t)(x)) /** Convert little-endian 64-bit int to host-endian */ #define le64toh(x) ((uint64_t)(x)) #endif /** * @} * * @name Read integer values from memory and convert to host format. * * @{ */ /** Read big-endian 16-bit int from memory */ static inline uint16_t be16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe16(tmp); } /** Read big-endian 32-bit int from memory */ static inline uint32_t be32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe32(tmp); } /** Read big-endian 64-bit int from memory */ static inline uint64_t be64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe64(tmp); } /** Read little-endian 16-bit int from memory */ static inline uint16_t le16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole16(tmp); } /** Read little-endian 32-bit int from memory */ static inline uint32_t le32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole32(tmp); } /** Read little-endian 64-bit int from memory */ static inline uint64_t le64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole64(tmp); } /** Read host-endian 16-bit int from memory */ static inline uint16_t h16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** Read host-endian 32-bit int from memory */ static inline uint32_t h32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** Read host-endian 64-bit int from memory */ static inline uint64_t h64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** * @} * * @name Convert host value to LE/BE and write to memory * * @{ */ /** Write big-endian 16-bit int to memory */ static inline void be16enc(void *p, uint16_t x) { uint16_t tmp = htobe16(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write big-endian 32-bit int to memory */ static inline void be32enc(void *p, uint32_t x) { uint32_t tmp = htobe32(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write big-endian 64-bit int to memory */ static inline void be64enc(void *p, uint64_t x) { uint64_t tmp = htobe64(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 16-bit int to memory */ static inline void le16enc(void *p, uint16_t x) { uint16_t tmp = htole16(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 32-bit int to memory */ static inline void le32enc(void *p, uint32_t x) { uint32_t tmp = htole32(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 64-bit int to memory */ static inline void le64enc(void *p, uint64_t x) { uint64_t tmp = htole64(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write host-endian 16-bit int to memory */ static inline void h16enc(void *p, uint16_t x) { memcpy(p, &x, sizeof(x)); } /** Write host-endian 32-bit int to memory */ static inline void h32enc(void *p, uint32_t x) { memcpy(p, &x, sizeof(x)); } /** Write host-endian 64-bit int to memory */ static inline void h64enc(void *p, uint64_t x) { memcpy(p, &x, sizeof(x)); } /** @} */ #endif /* _USUAL_ENDIAN_H_ */ pgqd/lib/usual/heap.h0000664000401600040160000000542313175113172013045 0ustar cbecbe/* * Binary Heap. * * Copyright (c) 2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Binary heap. * * Binary heap is sort of binary tree held inside array, * with following 2 properties: * - heap property: each node is "better" than it's childs. * - shape property: binary tree is complete, meaning all levels * except the last one are fully filled. * * Instead of "min"- or "max"-heap, this is "best"-heap, * as it operates with user-defined heap_is_better() functions, * which is used to bubble elements on top. */ #ifndef _USUAL_HEAP_H_ #define _USUAL_HEAP_H_ #include /** * Object comparision function. * * Should return true if a needs to reach top before b, * false if not or equal. */ typedef bool (*heap_is_better_f)(const void *a, const void *b); /** * Heap position storage. * * If user wants to delete elements from the middle of heap, * this function should be used to keep track where the element * is located. */ typedef void (*heap_save_pos_f)(void *a, unsigned pos); /** * Heap object. */ struct Heap; /** * Create new heap object. * * @param is_better_cb Callback to decide priority. * @param save_pos_cb Callback to store current index. * @param cx Allocation context. */ struct Heap *heap_create( heap_is_better_f is_better_cb, heap_save_pos_f save_pos_cb, CxMem *cx); /** Release memory allocated by heap */ void heap_destroy(struct Heap *h); /** Put new object into heap */ bool heap_push(struct Heap *h, void *ptr); /** Remove and return topmost object from heap */ void *heap_pop(struct Heap *h); /** Return topmost object in heap */ void *heap_top(struct Heap *h); /** Remove and return any object from heap by index */ void *heap_remove(struct Heap *h, unsigned pos); /** * Reserve room for more elements. * * Returns false if allocation failed. */ bool heap_reserve(struct Heap *h, unsigned extra); /** Return number of objects in heap */ unsigned heap_size(struct Heap *h); /* Return object by index, for testing */ void *heap_get_obj(struct Heap *h, unsigned pos); #endif pgqd/lib/usual/signal.h0000664000401600040160000000503413175113172013403 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Signals compat. * * general * - sigaction() -> signal() * * win32: * - SIGALRM, alarm(), signal(SIGALRM), sigaction(SIGALRM) * - kill(pid, 0) */ #ifndef _USUAL_SIGNAL_H_ #define _USUAL_SIGNAL_H_ #include #include /* * Compat sigval, detect based on siginfo_t.si_code. */ #if !defined(SI_QUEUE) && !defined(HAVE_SIGQUEUE) union sigval { int sival_int; void *sival_ptr; }; #endif /* * Compat sigevent */ #ifndef SIGEV_NONE #define SIGEV_NONE 0 #define SIGEV_SIGNAL 1 #define SIGEV_THREAD 2 struct sigevent { int sigev_notify; int sigev_signo; union sigval sigev_value; void (*sigev_notify_function)(union sigval); }; #endif /* * Compat sigaction() */ #ifndef HAVE_SIGACTION #define SA_SIGINFO 1 #define SA_RESTART 2 typedef struct siginfo_t siginfo_t; struct sigaction { union { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); }; int sa_flags; int sa_mask; }; #define sigemptyset(s) #define sigfillset(s) #define sigaddset(s, sig) #define sigdelset(s, sig) #define sigaction(a,b,c) compat_sigaction(a,b,c) int sigaction(int sig, const struct sigaction *sa, struct sigaction *old); #endif /* * win32 compat: * kill(), alarm, SIGALRM */ #ifdef WIN32 #define SIGALRM 1023 #define SIGBUS 1022 unsigned alarm(unsigned); int kill(int pid, int sig); typedef void (*_sighandler_t)(int); static inline _sighandler_t wrap_signal(int sig, _sighandler_t func) { /* sigaction has custom handling for SIGALRM */ if (sig == SIGALRM) { struct sigaction sa, oldsa; sa.sa_handler = func; sa.sa_flags = sa.sa_mask = 0; sigaction(SIGALRM, &sa, &oldsa); return oldsa.sa_handler; } else if (sig == SIGBUS) { return NULL; } return signal(sig, func); } #define signal(a,b) wrap_signal(a,b) #endif #endif pgqd/lib/usual/regex.c0000664000401600040160000006716213175113172013245 0ustar cbecbe/* * Small POSIX-only regex engine. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Simple recursive matcher, only features are small size * and POSIX compatibility. * * ERE syntax: . * ^ $ [] [[:cname:]] () {} | + ? * BRE syntax: . * ^ $ [] [[:cname:]] \(\) \{\} \1-9 * * With REG_RELAXED_SYNTAX, following common escapes will be available: * \b\B\d\D\s\S\w\W BRE: \| ERE: \1-9 * * With REG_RELAXED_MATCHING it returns the first match found after applying * leftmost-longest to all elements. It skips the combinatorics to turn it * into guaranteed-longest match. * * Skipped POSIX features: * - collation classes: [[. .]] * - equivalence classes: [[= =]] * - char ranges by locale order: [a-z] (byte order will be used) * - multi-byte chars: UTF-8 */ #include #ifndef USE_SYSTEM_REGEX #include #include #include #include #undef STRICT /* either dynamic or static decision */ #define STRICT (ctx->strict) /* how many regmatch_t can be reported */ #define MAX_GROUPS 128 /* max count we want to store, means 'infinite' for simple atoms */ #define MAX_COUNT 0x7fff /* max count for simple atoms: char, any or class */ #define SIMPLE_MAXCNT(op) (((op)->maxcnt == MAX_COUNT) ? 0x7FFFFFFF : (op)->maxcnt) #define is_word(c) (isalnum(c) || (c) == '_') struct Op; struct ExecCtx; struct GMatch; /* Operation type */ enum OpType { /* ops that take count */ OP_CHAR, OP_ANY, OP_CLASS, OP_GROUP, OP_BREF, /* ops that dont take count */ OP_BOL, OP_EOL, OP_WCHANGE, OP_NWCHANGE, OP_GMATCH, OP_FULLMATCH, }; #define NONCOUNT_OPS_START OP_BOL /* regex_t->internal */ struct RegexInt { struct Op *root; struct Op *glist; struct MemPool *pool; int flags; }; /* match function and its setter */ typedef int (*matcher_f)(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm); static void set_op_type(struct Op *op, enum OpType op_type); /* List of tokens to be AND-ed together */ struct AndList { struct AndList *next; struct Op *op_list; }; /* extra data for group Op */ struct GroupData { struct Op *parent; /* parent group or NULL for first group */ struct AndList *or_list;/* alternative AndLists */ struct Op *glist_prev; /* prev group Op */ bool has_refs; /* if bref references it */ }; /* char class data */ struct ClassData { uint32_t bitmap[256 / 32]; }; /* operation data */ struct Op { struct Op *next; matcher_f matcher; uint16_t mincnt; uint16_t maxcnt; uint8_t type; union { uint8_t grp_no; /* OP_GROUP: group nr, 0-toplevel */ char lit; /* OP_CHAR */ uint8_t bref; /* OP_BREF */ }; union { struct ClassData cdata; struct GroupData gdata; }; }; #define OP_BASE (offsetof(struct Op, cdata)) /* * Operations on ClassData */ static bool class_isset(const struct ClassData *cd, unsigned char c) { return cd->bitmap[c / 32] & (1 << (c % 32)); } static void class_set(struct ClassData *cd, unsigned char c) { cd->bitmap[c / 32] |= (1 << (c % 32)); } static void class_negate(struct ClassData *cd) { int i; class_set(cd, 0); for (i = 0; i < 256/32; i++) cd->bitmap[i] ^= -1; } /* * Parsing code */ /* top-level context */ struct ParseCtx { regex_t *rx; struct RegexInt *rxi; struct Op *last_group; struct AndList *last_andlist; struct Op *last_elem; /* last op in current OR branch */ bool gotcnt; /* count was attached to last op */ bool strict; /* strict syntax */ }; static struct AndList *new_andlist(struct ParseCtx *ctx, struct Op *g) { struct AndList *al = mempool_alloc(&ctx->rxi->pool, sizeof(*al)); if (!al) return NULL; if (ctx->last_andlist) { ctx->last_andlist->next = al; } else { g->gdata.or_list = al; } ctx->last_andlist = al; return al; } static struct Op *new_op(struct ParseCtx *ctx, enum OpType t, int extra) { struct Op *op = mempool_alloc(&ctx->rxi->pool, OP_BASE + extra); if (!op) return NULL; set_op_type(op, t); op->mincnt = op->maxcnt = 1; ctx->gotcnt = false; /* append */ if (ctx->last_elem) { ctx->last_elem->next = op; } else if (ctx->last_andlist) { ctx->last_andlist->op_list = op; } else if (ctx->last_group) { struct AndList *alist; alist = new_andlist(ctx, ctx->last_group); if (!alist) return NULL; alist->op_list = op; } ctx->last_elem = op; if (t == OP_GROUP) { struct Op *parent = ctx->last_group; int gno = ++ctx->rx->re_nsub; op->grp_no = gno; op->gdata.parent = parent; op->gdata.glist_prev = ctx->rxi->glist; ctx->rxi->glist = op; ctx->last_group = op; ctx->last_andlist = NULL; ctx->last_elem = NULL; if (!ctx->rxi->root) ctx->rxi->root = op; } return op; } static int op_char(struct ParseCtx *ctx, unsigned c) { struct Op *op = new_op(ctx, OP_CHAR, 0); if (!op) return REG_ESPACE; op->lit = c; if ((ctx->rxi->flags & REG_ICASE) && isalpha(c)) op->lit = tolower(c); return 0; } static int op_bref(struct ParseCtx *ctx, unsigned c) { struct Op *g, *op; op = new_op(ctx, OP_BREF, 0); if (!op) return REG_ESPACE; op->bref = c - '0'; /* check if valid ref */ for (g = ctx->last_group; g; g = g->gdata.parent) { if (g->grp_no == op->bref) return REG_ESUBREG; } /* tag the group as referenced */ for (g = ctx->rxi->glist; g; g = g->gdata.glist_prev) { if (g->grp_no == op->bref) { g->gdata.has_refs = true; return 0; } } return REG_ESUBREG; } static int op_simple(struct ParseCtx *ctx, enum OpType t) { struct Op *op = new_op(ctx, t, 0); if (!op) return REG_ESPACE; return 0; } static int op_count_simple(struct ParseCtx *ctx, int min, int max) { struct Op *op = ctx->last_elem; if (!op || ctx->gotcnt) return REG_BADRPT; if (op->type >= NONCOUNT_OPS_START) return REG_BADRPT; ctx->gotcnt = true; op->mincnt = min; op->maxcnt = max; return 0; } static int op_count_full(struct ParseCtx *ctx, const char **re) { unsigned a, b; char *end = (char *)*re; bool ext = ctx->rxi->flags & REG_EXTENDED; int err; /* apply sanity check */ err = op_count_simple(ctx, 1, 1); if (err) return err; /* parse */ a = b = strtoul(*re, &end, 10); if (end == *re) return REG_EBRACE; if (*end == ',') { *re = end + 1; end = (char*)*re; b = strtoul(*re, &end, 10); if (end == *re) b = MAX_COUNT; } if (a > b || b > MAX_COUNT || a >= MAX_COUNT) return REG_BADBR; /* check for correct termination */ if (ext && end[0] == '}') { *re = end + 1; goto done; } else if (!ext && end[0] == '\\' && end[1] == '}') { *re = end + 2; goto done; } /* bad fmt, decide between error codes */ return strchr(end, '}') ? REG_BADBR : REG_EBRACE; done: ctx->last_elem->mincnt = a; ctx->last_elem->maxcnt = b; return 0; } static int op_gstart(struct ParseCtx *ctx) { struct Op *op; op = new_op(ctx, OP_GROUP, sizeof(struct GroupData)); if (!op) return REG_ESPACE; if (op->grp_no >= MAX_GROUPS) return REG_BADPAT; return 0; } static int finish_branch(struct ParseCtx *ctx) { int err; /* disallow empty OR fragments, but not empty groups */ if (!ctx->last_elem && ctx->last_andlist && STRICT) return REG_BADPAT; if (ctx->last_group->gdata.parent) err = op_simple(ctx, OP_GMATCH); else err = op_simple(ctx, OP_FULLMATCH); if (err) return err; ctx->last_elem = NULL; return 0; } static int op_gend(struct ParseCtx *ctx) { struct Op *op = ctx->last_group; struct AndList *alist; int err; if (!op) return REG_EPAREN; err = finish_branch(ctx); if (err) return err; ctx->last_group = op->gdata.parent; ctx->last_elem = op; /* recover previous andlist... */ alist = ctx->last_group->gdata.or_list; while (alist && alist->next) alist = alist->next; ctx->last_andlist = alist; return 0; } static int op_or(struct ParseCtx *ctx) { struct Op *gop = ctx->last_group; struct AndList *alist; int err; /* disallow empty OR branches */ if (!ctx->last_elem && STRICT) return REG_BADPAT; /* start new branch */ err = finish_branch(ctx); if (err) return err; alist = new_andlist(ctx, gop); if (!alist) return REG_ESPACE; ctx->last_andlist = alist; ctx->last_elem = NULL; return 0; } /* * Parse bracketed classes. */ static void add_char(struct ClassData *cd, unsigned char c, bool icase) { if (icase && isalpha(c)) { class_set(cd, tolower(c)); class_set(cd, toupper(c)); } else { class_set(cd, c); } } struct NamedClass { const char name[7]; unsigned char name_len; int (*check_func)(int c); }; static const struct NamedClass ctype_list[] = { { "alnum", 5, isalnum }, { "alpha", 5, isalpha }, { "blank", 5, isblank }, { "cntrl", 5, iscntrl }, { "digit", 5, isdigit }, { "graph", 5, isgraph }, { "lower", 5, islower }, { "print", 5, isprint }, { "punct", 5, ispunct }, { "space", 5, isspace }, { "upper", 5, isupper }, { "xdigit", 6, isxdigit }, }; static int fill_class(struct ClassData *cd, const char *name, const char **s_p, bool icase) { unsigned c; const struct NamedClass *cc = ctype_list; for (c = 0; c < ARRAY_NELEM(ctype_list); c++) { cc = ctype_list + c; if (strncmp(name, cc->name, cc->name_len) != 0) continue; name += cc->name_len; if (name[0] == ':' && name[1] == ']') goto found; break; } return *name ? REG_ECTYPE : REG_EBRACK; found: /* fill map */ for (c = 1; c < 256; c++) { if (cc->check_func(c)) add_char(cd, c, icase); } *s_p = name + 2; return 0; } #define MAP_RANGE 0x7FFF0001 #define MAP_END 0x7FFF0002 #define MAP_OTHER 0x7FFF0003 static int get_map_token(struct ParseCtx *ctx, const char **s_p, unsigned *dst_p, bool start, struct ClassData *cd, bool icase) { const char *s = *s_p; unsigned res; if (*s == '-') { if (start || s[1] == ']') res = '-'; else res = MAP_RANGE; s += 1; } else if (*s == ']' && !start) { res = MAP_END; s++; } else if (*s == '[' && (s[1] == '.' || s[1] == ':' || s[1] == '=')) { if (s[1] == ':') { s += 2; *dst_p = MAP_OTHER; return fill_class(cd, s, s_p, icase); } return REG_BADPAT; } else { res = (unsigned char)*s++; } *dst_p = res; *s_p = s; return 0; } static int op_class(struct ParseCtx *ctx, const char **re) { const char *s = *re; struct ClassData *cd; struct Op *op; bool not = false, icase = ctx->rxi->flags & REG_ICASE; const char *start; unsigned tk, c, prevtk = 0; bool is_range = false; int err; if (*s == '^') { s++; not = true; } start = s; op = new_op(ctx, OP_CLASS, sizeof(struct ClassData)); if (!op) return REG_ESPACE; cd = &op->cdata; if (not && (ctx->rxi->flags & REG_NEWLINE)) class_set(cd, '\n'); while (*s) { err = get_map_token(ctx, &s, &tk, s == start, cd, icase); if (err) return err; if (tk == MAP_END) { if (prevtk) add_char(cd, prevtk, icase); goto done; } else if (tk == MAP_OTHER) { if (is_range) return REG_ERANGE; if (prevtk) add_char(cd, prevtk, icase); prevtk = 0; } else if (tk == MAP_RANGE) { if (!prevtk) return REG_ERANGE; is_range = true; } else if (is_range) { if (tk < prevtk) return REG_ERANGE; for (c = prevtk; c <= tk; c++) add_char(cd, c, icase); is_range = false; prevtk = 0; } else { if (prevtk) add_char(cd, prevtk, icase); prevtk = tk; } } return REG_EBRACK; done: *re = s; if (not) class_negate(cd); return 0; } static int op_class_const(struct ParseCtx *ctx, const char *def) { const char *p = def + 1; return op_class(ctx, &p); } /* * Top-level syntax */ static int parse_relaxed_escapes(struct ParseCtx *ctx, char c) { if (STRICT) return REG_BADPAT; switch (c) { case 'b': return op_simple(ctx, OP_WCHANGE); case 'B': return op_simple(ctx, OP_NWCHANGE); case 'w': return op_class_const(ctx, "[_[:alnum:]]"); case 'W': return op_class_const(ctx, "[^_[:alnum:]]"); case 'd': return op_class_const(ctx, "[[:digit:]]"); case 'D': return op_class_const(ctx, "[^[:digit:]]"); case 's': return op_class_const(ctx, "[[:space:]]"); case 'S': return op_class_const(ctx, "[^[:space:]]"); } return REG_BADPAT; } static int parse_posix_ext(struct ParseCtx *ctx, const char *re) { int err = 0; unsigned c; int glevel = 0; loop: if (err) return err; c = *re++; switch (c) { case 0: return (glevel == 0) ? 0 : REG_EPAREN; case '(': glevel++; err = op_gstart(ctx); break; case ')': if (glevel > 0) { glevel--; err = op_gend(ctx); } else { err = op_char(ctx, c); /* POSIX bug */ } break; case '|': err = op_or(ctx); break; case '*': err = op_count_simple(ctx, 0, MAX_COUNT); break; case '?': err = op_count_simple(ctx, 0, 1); break; case '+': err = op_count_simple(ctx, 1, MAX_COUNT); break; case '[': err = op_class(ctx, &re); break; case '{': err = op_count_full(ctx, &re); break; case '.': err = op_simple(ctx, OP_ANY); break; case '^': err = op_simple(ctx, OP_BOL); break; case '$': err = op_simple(ctx, OP_EOL); break; case '\\': goto escaped; default: err = op_char(ctx, c); } goto loop; escaped: c = *re++; if (c == 0) err = REG_EESCAPE; else if (c >= '0' && c <= '9') err = STRICT ? REG_BADPAT : op_bref(ctx, c); else if (isalpha(c)) err = parse_relaxed_escapes(ctx, c); else err = op_char(ctx, c); goto loop; } static int parse_posix_basic(struct ParseCtx *ctx, const char *re) { int err = 0; unsigned c; int glevel = 0; loop: if (err) return err; c = *re++; switch (c) { case 0: return (glevel == 0) ? 0 : REG_EPAREN; case '*': if (ctx->last_elem && ctx->last_elem->type != OP_BOL) err = op_count_simple(ctx, 0, MAX_COUNT); else err = op_char(ctx, '*'); break; case '.': err = op_simple(ctx, OP_ANY); break; case '[': err = op_class(ctx, &re); break; case '^': if (!ctx->last_elem) err = op_simple(ctx, OP_BOL); else err = op_char(ctx, c); break; case '$': if (!*re || (re[0] == '\\' && re[1] == ')')) err = op_simple(ctx, OP_EOL); else err = op_char(ctx, c); break; case '\\': goto escaped; default: err = op_char(ctx, c); } goto loop; escaped: c = *re++; switch (c) { case 0: return REG_EESCAPE; case '(': glevel++; err = op_gstart(ctx); break; case ')': glevel--; if (glevel < 0) return REG_EPAREN; err = op_gend(ctx); break; case '{': err = op_count_full(ctx, &re); break; case '.': case '^': case '$': case '*': case '[': case ']': case '\\': err = op_char(ctx, c); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': err = op_bref(ctx, c); break; case '|': err = STRICT ? REG_BADPAT : op_or(ctx); break; default: err = parse_relaxed_escapes(ctx, c); } goto loop; } /* * Public compiling API. */ int regcomp(regex_t *rx, const char *re, int flags) { struct ParseCtx ctx; struct RegexInt *rxi; int err; struct MemPool *pool = NULL; /* do it first, to allow regfree() */ memset(rx, 0, sizeof(*rx)); if (flags & ~(REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE | REG_RELAXED)) return REG_BADPAT; if (!*re) return REG_BADPAT; rxi = mempool_alloc(&pool, sizeof(*rxi)); if (!rxi) return REG_ESPACE; rx->internal = rxi; rxi->pool = pool; /* initialize rx and local context */ memset(&ctx, 0, sizeof(ctx)); ctx.rx = rx; ctx.rxi = rxi; ctx.strict = !(flags & REG_RELAXED_SYNTAX); rxi->flags = flags; /* setup group #0 */ rx->re_nsub = -1; err = op_gstart(&ctx); if (err) goto failed; /* launch proper parser */ if (flags & REG_EXTENDED) err = parse_posix_ext(&ctx, re); else err = parse_posix_basic(&ctx, re); /* finalize group #0 */ if (!err) err = finish_branch(&ctx); /* relax if details are not needed */ if (flags & REG_NOSUB) { rxi->flags |= REG_RELAXED_MATCHING; rx->re_nsub = 0; } failed: /* clean up if problems */ if (err) regfree(rx); return err; } /* * Matching code */ /* historical best match */ struct HMatch { const char *hist_start; const char *hist_end; int rep_len; /* if repeated seq, full len thus far */ }; /* per-group-match context */ struct GMatch { struct GMatch *parent; /* parent group */ const struct Op *owner; /* Op for this group */ const char *start; /* match start */ const char *end; /* match end, NULL if no match */ struct GMatch *prevgm; /* older stack entry */ struct HMatch hm_next; /* best match for following stack entry */ int count; /* match nr in repeated seq */ }; /* top context */ struct ExecCtx { const regex_t *rx; const struct RegexInt *rxi; const char *str_start; regmatch_t *pmatch; int nmatch; int flags; bool strict; const char *last_endpos; struct HMatch hm_first[MAX_GROUPS]; struct GMatch *gm_stack[MAX_GROUPS]; struct GMatch *gm_cache[MAX_GROUPS]; }; static void push_gm(struct ExecCtx *ctx, struct GMatch *gm) { int gno = gm->owner->grp_no; gm->prevgm = ctx->gm_stack[gno]; ctx->gm_stack[gno] = gm; } static void pop_gm(struct ExecCtx *ctx, struct GMatch *gm) { int gno = gm->owner->grp_no; ctx->gm_stack[gno] = gm->prevgm; } static inline int do_match(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { return op->matcher(ctx, op, str, gm); } static int scan_next(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm, int curcnt, int alen) { int err = REG_NOMATCH; bool gotmatch = false; if (curcnt == op->mincnt) return do_match(ctx, op->next, str, gm); for (; curcnt >= op->mincnt; curcnt--) { err = do_match(ctx, op->next, str, gm); if (STRICT && err == 0) gotmatch = true; else if (err != REG_NOMATCH) break; str -= alen; } if (err == REG_NOMATCH && gotmatch) err = 0; return err; } static int match_char(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool icase = (ctx->flags & REG_ICASE); int c, i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt) && str[i]; i++) { c = icase ? tolower((unsigned char)str[i]) : str[i]; if (c != op->lit) break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_any(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool nl = (ctx->flags & REG_NEWLINE); int i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt) && str[i]; i++) { if (nl && str[i] == '\n') break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_class(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { int i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt); i++) { if (!class_isset(&op->cdata, str[i])) break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_bol(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { if (str == ctx->str_start && !(ctx->flags & REG_NOTBOL)) return do_match(ctx, op->next, str, gm); else if (str != ctx->str_start && str[-1] == '\n' && (ctx->flags & REG_NEWLINE)) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_eol(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { if (*str == '\n' && (ctx->flags & REG_NEWLINE)) return do_match(ctx, op->next, str, gm); else if (*str == 0 && !(ctx->flags & REG_NOTEOL)) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_wchange(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool prevw = (str == ctx->str_start) ? false : is_word(str[-1]); bool curw = is_word(str[0]); bool ischange = prevw ^ curw; if ((op->type == OP_WCHANGE) ? ischange : !ischange) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_bref(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool icase = ctx->flags & REG_ICASE; int i; struct GMatch *bgm = ctx->gm_stack[op->bref]; int blen = (bgm && bgm->end) ? (bgm->end - bgm->start) : -1; /* handle no-match, zero-len, zero-count */ if (blen < 0 && op->mincnt > 0) return REG_NOMATCH; if (blen <= 0 || op->maxcnt == 0) return do_match(ctx, op->next, str, gm); /* find max matches */ for (i = 0; (i < op->maxcnt) && *str; i++) { if (icase && strncasecmp(str, bgm->start, blen) != 0) break; else if (!icase && strncmp(str, bgm->start, blen) != 0) break; str += blen; } return scan_next(ctx, op, str, gm, i, blen); } static int match_group(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { int err = REG_NOMATCH; bool gotmatch = false; struct GMatch gthis; /* per-group-match context */ memset(>his, 0, sizeof(gthis)); gthis.owner = op; gthis.start = str; gthis.parent = gm; if (gm && gm->owner == op) { gthis.parent = gm->parent; gthis.count = gm->count + 1; } gm = >his; push_gm(ctx, gm); if (op->maxcnt > 0) { struct AndList *alist = op->gdata.or_list; /* check all branches, unless relaxed matching */ while (alist) { err = do_match(ctx, alist->op_list, str, gm); if (err == 0 && STRICT) { gm->end = NULL; gotmatch = true; } else if (err != REG_NOMATCH) break; alist = alist->next; } } /* is no-match allowed? */ if ((op->mincnt == 0) && (gm->count == 0) && (err == REG_NOMATCH || (err == 0 && STRICT))) { gm->end = NULL; err = do_match(ctx, op->next, str, gm->parent); } pop_gm(ctx, gm); return gotmatch ? 0 : err; } static int match_gend(struct ExecCtx *ctx, const struct Op *f_op, const char *str, struct GMatch *gm) { int err = REG_NOMATCH; const struct Op *op = gm->owner; bool zeromatch = (str == gm->start); bool gotmatch = false; /* ignore follow-up empty matches, unless it has backrefs */ if (zeromatch && gm->count > 0 && gm->count >= op->mincnt && !gm->owner->gdata.has_refs) return REG_NOMATCH; /* tag as matched */ gm->end = str; /* try more repeats, stop if count full or last match was zero-length */ if (gm->count + 1 < op->maxcnt && !zeromatch) { err = match_group(ctx, op, str, gm); if (err == 0 && STRICT) gotmatch = true; else if (err != REG_NOMATCH) return err; } /* fail if not enough repeats */ if (!zeromatch && gm->count + 1 < op->mincnt) return err; /* continue with parent branch */ err = do_match(ctx, op->next, str, gm->parent); if (err == REG_NOMATCH && gotmatch) err = 0; return err; } /* * The juice of POSIX - match weighting. */ static int gmatch_hist_cmp(struct ExecCtx *ctx, int gno, struct GMatch *gm, int replen) { struct HMatch *hm = (gm->prevgm) ? &gm->prevgm->hm_next : &ctx->hm_first[gno]; int gmlen = (gm->end) ? (gm->end - gm->start) : -1; int hmlen = (hm->hist_end) ? (hm->hist_end - hm->hist_start) : -1; int gmreplen = (gmlen >= 0) ? (gmlen + replen) : replen; int hmreplen = ((hmlen >= 0) ? hmlen : 0) + hm->rep_len; int gmofs = (gm->end) ? (gm->start - ctx->str_start) : -1; int hmofs = (hm->hist_start) ? (hm->hist_start - ctx->str_start) : -1; /* prefer rightmost match, to allow preceding elements match more */ int res = (gmofs - hmofs); /* prefer longer repeated match */ if (res == 0 && gm->count == 0) res = (gmreplen - hmreplen); /* prefer longer single match */ if (res == 0) res = (gmlen - hmlen); return res; } static int cmp_gmatches(struct ExecCtx *ctx, int gno, struct GMatch *gm, int replen) { int cmp = 0, gmlen; if (gm) { /* need to compare preceding groups first */ gmlen = gm->end ? gm->end - gm->start : 0; cmp = cmp_gmatches(ctx, gno, gm->prevgm, (gm->count == 0) ? 0 : (replen + gmlen)); /* actual comparision */ if (!cmp) cmp = gmatch_hist_cmp(ctx, gno, gm, replen); } return cmp; } static int gm_resolve_tie(struct ExecCtx *ctx, int gno) { struct GMatch *gm = ctx->gm_stack[gno]; if (!gm) /* 0-count match is better than no match */ return ctx->hm_first[gno].hist_start ? -1 : 0; return cmp_gmatches(ctx, gno, gm, 0); } static void fill_history(struct ExecCtx *ctx, int gno) { struct HMatch *hm; int gmlen, rep_len = 0; struct GMatch *gm = ctx->gm_stack[gno]; while (STRICT && gm) { hm = (gm->prevgm) ? &gm->prevgm->hm_next : &ctx->hm_first[gno]; hm->hist_start = gm->start; hm->hist_end = gm->end; hm->rep_len = rep_len; gmlen = gm->end ? (gm->end - gm->start) : 0; rep_len += gmlen; if (gm->count == 0) rep_len = 0; gm = gm->prevgm; } } static void publish_gm(struct ExecCtx *ctx, int gno) { struct GMatch *gm = ctx->gm_stack[gno]; regmatch_t *rm = ctx->pmatch + gno; /* ignore non-matches */ while (gm && !gm->end) gm = gm->prevgm; /* require it to be inside reported parent */ if (gm && gm->parent) { int pno = gm->parent->owner->grp_no; if (gm->parent != ctx->gm_cache[pno]) gm = NULL; } ctx->gm_cache[gno] = gm; /* publish new match */ if (gm) { rm->rm_so = gm->start - ctx->str_start; rm->rm_eo = gm->end - ctx->str_start; } else { rm->rm_so = -1; rm->rm_eo = -1; } } /* compare and publish */ static int got_full_match(struct ExecCtx *ctx, const struct Op *f_op, const char *str, struct GMatch *gm) { int gno, cmp; /* tag group as matched */ gm->end = str; /* ignore shorter matches */ if (ctx->last_endpos && str < ctx->last_endpos) return 0; /* longer or equal length */ if (str > ctx->last_endpos) { ctx->last_endpos = str; goto better_match; } else if (STRICT && ctx->nmatch > 1) { for (gno = 0; gno < ctx->nmatch; gno++) { cmp = gm_resolve_tie(ctx, gno); if (cmp < 0) break; if (cmp > 0) goto better_match; } } return 0; better_match: for (gno = 0; gno < ctx->nmatch; gno++) { publish_gm(ctx, gno); fill_history(ctx, gno); } return 0; } /* fill in proper matcher */ static void set_op_type(struct Op *op, enum OpType op_type) { static const matcher_f mlist[] = { match_char, match_any, match_class, match_group, match_bref, match_bol, match_eol, match_wchange, match_wchange, match_gend, got_full_match }; op->matcher = mlist[op_type]; op->type = op_type; } /* * Public matching API */ int regexec(const regex_t *rx, const char *str, size_t nmatch, regmatch_t pmatch[], int eflags) { int err; struct ExecCtx ctx; if (eflags & ~(REG_NOTBOL | REG_NOTEOL)) return REG_BADPAT; /* init local context */ memset(&ctx, 0, sizeof(ctx)); ctx.pmatch = pmatch; ctx.nmatch = nmatch; ctx.str_start = str; ctx.rx = rx; ctx.rxi = rx->internal; ctx.flags = ctx.rxi->flags | eflags; /* reset pmatch area */ if (!(ctx.flags & REG_NOSUB)) memset(pmatch, -1, nmatch * sizeof(regmatch_t)); /* decide pmatch area that will be used */ if (!pmatch || (ctx.flags & REG_NOSUB)) ctx.nmatch = 0; else if (nmatch > (size_t)rx->re_nsub + 1) ctx.nmatch = rx->re_nsub + 1; ctx.strict = !(ctx.flags & REG_RELAXED_MATCHING) && (ctx.nmatch > 0); /* execute search */ str--; do { str++; err = do_match(&ctx, ctx.rxi->root, str, NULL); } while ((err == REG_NOMATCH) && *str); return err; } /* * Free parse tree */ void regfree(regex_t *rx) { struct RegexInt *rxi; if (rx) { rxi = rx->internal; if (rxi) mempool_destroy(&rxi->pool); memset(rx, 0, sizeof(*rx)); } } /* * Error strings */ size_t regerror(int err, const regex_t *rx, char *dst, size_t dstlen) { static const char errlist[][9] = { "NOERROR", /* 0 */ "NOMATCH", /* 1 */ "BADBR", /* 2 */ "BADPAT", /* 3 */ "BADRPT", /* 4 */ "EBRACE", /* 5 */ "EBRACK", /* 6 */ "ECOLLATE", /* 7 */ "ECTYPE", /* 8 */ "EESCAPE", /* 9 */ "EPAREN", /* 10 */ "ERANGE", /* 11 */ "ESPACE", /* 12 */ "ESUBREG", /* 13 */ }; const char *s = "EUNKNOWN"; if ((size_t)err < ARRAY_NELEM(errlist)) s = errlist[err]; return snprintf(dst, dstlen, "%s", s); } #endif /* !USE_SYSTEM_REGEX */ pgqd/lib/usual/netdb.c0000664000401600040160000001104613175113172013215 0ustar cbecbe/* * libusual - Utility library for C * * Copyright (c) 2010 Marko Kreen, Skype Technologies * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* is compat function needed? */ #ifndef HAVE_GETADDRINFO_A /* full compat if threads are available */ #ifdef HAVE_PTHREAD #include #include /* * Basic blocking lookup */ static void gaia_lookup(pthread_t origin, struct gaicb *list[], int nitems, struct sigevent *sevp) { struct gaicb *g; int i, res; for (i = 0; i < nitems; i++) { g = list[i]; res = getaddrinfo(g->ar_name, g->ar_service, g->ar_request, &g->ar_result); g->_state = res; } if (!sevp || sevp->sigev_notify == SIGEV_NONE) { /* do nothing */ } else if (sevp->sigev_notify == SIGEV_SIGNAL) { /* send signal */ pthread_kill(origin, sevp->sigev_signo); } else if (sevp->sigev_notify == SIGEV_THREAD) { /* call function */ sevp->sigev_notify_function(sevp->sigev_value); } } /* * Thread to run blocking lookup in */ struct GAIAContext { struct List req_list; pthread_cond_t cond; pthread_mutex_t lock; pthread_t thread; }; struct GAIARequest { struct List node; pthread_t origin; int nitems; struct sigevent sev; struct gaicb *list[FLEX_ARRAY]; }; #define RQ_SIZE(n) (offsetof(struct GAIARequest,list) + (n)*(sizeof(struct gaicb *))) static void gaia_lock_reqs(struct GAIAContext *ctx) { pthread_mutex_lock(&ctx->lock); } static void gaia_unlock_reqs(struct GAIAContext *ctx) { pthread_mutex_unlock(&ctx->lock); } static void *gaia_lookup_thread(void *arg) { struct GAIAContext *ctx = arg; struct GAIARequest *rq; struct List *el; gaia_lock_reqs(ctx); while (1) { el = list_pop(&ctx->req_list); if (!el) { pthread_cond_wait(&ctx->cond, &ctx->lock); continue; } gaia_unlock_reqs(ctx); rq = container_of(el, struct GAIARequest, node); gaia_lookup(rq->origin, rq->list, rq->nitems, &rq->sev); free(rq); gaia_lock_reqs(ctx); } return NULL; } /* * Functions run in user thread */ static int gaia_post_request(struct GAIAContext *ctx, struct gaicb *list[], int nitems, struct sigevent *sevp) { struct GAIARequest *rq; rq = malloc(RQ_SIZE(nitems)); if (!rq) return EAI_MEMORY; list_init(&rq->node); rq->origin = pthread_self(); rq->nitems = nitems; if (sevp) rq->sev = *sevp; else rq->sev.sigev_notify = SIGEV_NONE; memcpy(rq->list, list, sizeof(struct gaicb *)); gaia_lock_reqs(ctx); list_append(&ctx->req_list, &rq->node); gaia_unlock_reqs(ctx); pthread_cond_signal(&ctx->cond); return 0; } static struct GAIAContext *gaia_create_context(void) { struct GAIAContext *ctx; int err; ctx = malloc(sizeof(*ctx)); if (!ctx) return NULL; list_init(&ctx->req_list); err = pthread_cond_init(&ctx->cond, NULL); if (err) goto failed; err = pthread_mutex_init(&ctx->lock, NULL); if (err) goto failed; err = pthread_create(&ctx->thread, NULL, gaia_lookup_thread, ctx); if (err) goto failed; return ctx; failed: free(ctx); errno = err; return NULL; } /* * Final interface */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) { static struct GAIAContext *ctx; if (nitems <= 0) return 0; if (sevp && sevp->sigev_notify != SIGEV_NONE && sevp->sigev_notify != SIGEV_SIGNAL && sevp->sigev_notify != SIGEV_THREAD) goto einval; if (mode == GAI_WAIT) { gaia_lookup(pthread_self(), list, nitems, sevp); return 0; } else if (mode == GAI_NOWAIT) { if (!ctx) { ctx = gaia_create_context(); if (!ctx) return EAI_MEMORY; } return gaia_post_request(ctx, list, nitems, sevp); } einval: errno = EINVAL; return EAI_SYSTEM; } #else /* without threads not much to do */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) { errno = ENOSYS; return EAI_SYSTEM; } #endif /* !HAVE_PTHREAD_H */ #endif /* !HAVE_GETADDRINFO_A */ pgqd/lib/usual/regex.h0000664000401600040160000001533013175113172013240 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * POSIX regular expession API, provided by either libc or internally. * * The internal regex engine is only activated if OS does not provide * @ref uregex_links "" (eg. Windows) or if * --with-internal-regex is used when configuring @ref libusual. * * @section uregex Features of internal regex (uregex). * * Simple recursive matcher, only features are small size * and POSIX compatibility. Supports both Extended Regular Expressions (ERE) * and Basic Regular Expressions (BRE). * * @section uregex_syntax Supported syntax * @code * Both: . * ^ $ [] [[:cname:]] * ERE: () {} | + ? * BRE: \(\) \{\} \1-9 * @endcode * * With REG_RELAXED_SYNTAX, following common escapes will be available: * @code * Both: \b\B\d\D\s\S\w\W * BRE: \| * ERE: \1-9 * @endcode * * With REG_RELAXED_MATCHING it returns the first match found after applying * leftmost-longest to all elements. It skips the combinatorics to turn it * into guaranteed-longest match. * * @section uregex_skip Skipped POSIX features * - collation classes: [[. .]] * - equivalence classes: [[= =]] * - char ranges by locale order: [a-z] (byte order will be used) * - multi-byte chars: UTF-8 * * @section uregex_globaldefs Global defines * - USUAL_RELAXED_REGEX * - USE_INTERNAL_REGEX * * @section uregex_links Compatibility * * - * POSIX-2008 spec - by default uRegex run in mode where only * features specified by POSIX are available. * * - * AT\&T Research regex(3) regression tests - uRegex follows the interpretation * given there and fully passes the testsuite. */ #ifndef _USUAL_REGEX_H_ #define _USUAL_REGEX_H_ #include #if !defined(USE_INTERNAL_REGEX) && defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) #define USE_SYSTEM_REGEX #endif #ifdef USE_SYSTEM_REGEX #include #else /* * uRegex defines */ /** * @name Standard flags to regcomp() * @{ */ /** Use POSIX Extended Regex Syntax instead of Basic Syntax */ #define REG_EXTENDED (1 << 0) /** Do case-insensitive matching */ #define REG_ICASE (1 << 1) /** Do case-insensitive matching */ #define REG_NOSUB (1 << 2) /** Do case-insensitive matching */ #define REG_NEWLINE (1 << 3) /* @} */ /** * @name Standard flags to regexec() * @{ */ /** The start of string is not beginning of line, so ^ should not match */ #define REG_NOTBOL (1 << 4) /** The end of string is not end of line, so $ should not match */ #define REG_NOTEOL (1 << 5) /* @} */ /** * @name Standard error codes * @{ */ /** Match not found */ #define REG_NOMATCH 1 /** Bad {} repeat specification */ #define REG_BADBR 2 /** General problem with regular expression */ #define REG_BADPAT 3 /** Repeat used without preceding non-repeat element */ #define REG_BADRPT 4 /** Syntax error with {} */ #define REG_EBRACE 5 /** Syntax error with [] */ #define REG_EBRACK 6 /** Bad collation reference */ #define REG_ECOLLATE 7 /** Bad character class reference */ #define REG_ECTYPE 8 /** Trailing backslack */ #define REG_EESCAPE 9 /** Syntax error with () */ #define REG_EPAREN 10 /** Bad endpoint in range */ #define REG_ERANGE 11 /** No memory */ #define REG_ESPACE 12 /** Bad subgroup reference */ #define REG_ESUBREG 13 /* @} */ /** * @name Other defines * @{ */ #undef RE_DUP_MAX /** Max count user can enter via {} */ #define RE_DUP_MAX 0x7ffe /* @} */ /** * @name Non-standard flags for regcomp() * @{ */ /** * Allow few common non-standard escapes: * @code * \b - word-change * \B - not word change * \d - digit * \D - non-digit * \s - space * \S - non-space * \w - word char * \W - non-word char * \/ - / * @endcode */ #define REG_RELAXED_SYNTAX (1 << 14) /** * Dont permute groups in attempt to get longest match. * * May give minor speed win at the expense of strict * POSIX compatibility. */ #define REG_RELAXED_MATCHING (1 << 15) /** Turn on both REG_RELAXED_SYNTAX and REG_RELAXED_MATCHING */ #define REG_RELAXED (REG_RELAXED_SYNTAX | REG_RELAXED_MATCHING) /* @} */ /* turn them permanently on */ #ifdef USUAL_RELAXED_REGEX #undef REG_EXTENDED #define REG_EXTENDED (1 | REG_RELAXED) #endif /** * Compiled regex. * * It has only one standard field - re_nsub, * rest are implementation-specific. */ typedef struct { /** Number of subgroups in expression */ int re_nsub; void *internal; } regex_t; /** Type for offset in match */ typedef long regoff_t; /** Match location */ typedef struct { regoff_t rm_so; /**< Start offset */ regoff_t rm_eo; /**< End offset */ } regmatch_t; /* avoid name conflicts */ #define regcomp(a,b,c) usual_regcomp(a,b,c) #define regexec(a,b,c,d,e) usual_regexec(a,b,c,d,e) #define regerror(a,b,c,d) usual_regerror(a,b,c,d) #define regfree(a) usual_regfree(a) /** * Compile regex. * * @param rx Pre-allocated @ref regex_t structure to fill. * @param re Regex as zero-terminated string. * @param flags See above for regcomp() flags. */ int regcomp(regex_t *rx, const char *re, int flags); /** * Execute regex on a string. * * @param rx Regex previously initialized with regcomp() * @param str Zero-terminated string to match * @param nmatch Number of matches in pmatch * @param pmatch Array of matches. * @param eflags Execution flags. Supported flags: @ref REG_NOTBOL, @ref REG_NOTEOL */ int regexec(const regex_t *rx, const char *str, size_t nmatch, regmatch_t pmatch[], int eflags); /** * Give error description. * * @param err Error code returned by regcomp() or regexec() * @param rx Regex structure used in regcomp() or regexec() * @param dst Destination buffer * @param dstlen Size of dst */ size_t regerror(int err, const regex_t *rx, char *dst, size_t dstlen); /** * Free resources allocated by regcomp(). * @param rx Regex previously filled by regcomp() */ void regfree(regex_t *rx); #endif /* !USE_SYSTEM_REGEX */ #endif /* _USUAL_REGEX_H_ */ pgqd/lib/usual/pgsocket.c0000664000401600040160000002016713175113172013744 0ustar cbecbe/* * Async Postgres connection. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #define MAX_QRY_ARGS 32 /* PgSocket.wait_type */ enum WType { W_NONE = 0, W_SOCK, W_TIME }; typedef void (*libev_cb)(int sock, short flags, void *arg); struct PgSocket { /* libevent state */ struct event ev; /* track wait state */ enum WType wait_type; /* EV_READ / EV_WRITE */ uint8_t wait_event; /* should connect after sleep */ bool reconnect; /* current connection */ PGconn *con; /* user handler */ pgs_handler_f handler_func; void *handler_arg; /* saved connect string */ const char *connstr; /* custom base or NULL */ struct event_base *base; /* temp place for resultset */ PGresult *last_result; usec_t connect_time; usec_t lifetime; }; /* report event to user callback */ static void send_event(struct PgSocket *db, enum PgEvent ev) { db->handler_func(db, db->handler_arg, ev, NULL); } /* wait socket event from libevent */ static void wait_event(struct PgSocket *db, short ev, libev_cb fn) { Assert(!db->wait_type); event_set(&db->ev, PQsocket(db->con), ev, fn, db); if (db->base) event_base_set(db->base, &db->ev); if (event_add(&db->ev, NULL) < 0) fatal_perror("event_add"); db->wait_type = W_SOCK; db->wait_event = ev; } /* wait timeout from libevent */ static void timeout_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; db->wait_type = W_NONE; if (db->reconnect) { db->reconnect = false; pgs_connect(db); } else { send_event(db, PGS_TIMEOUT); } } /* some error happened */ static void conn_error(struct PgSocket *db, enum PgEvent ev, const char *desc) { log_error("connection error: %s", desc); log_error("libpq: %s", PQerrorMessage(db->con)); send_event(db, ev); } /* report previously stored result */ static void report_last_result(struct PgSocket *db) { PGresult *res = db->last_result; if (!res) return; db->last_result = NULL; switch (PQresultStatus(res)) { default: log_error("%s: %s", PQdb(db->con), PQresultErrorMessage(res)); case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: case PGRES_COPY_OUT: case PGRES_COPY_IN: db->handler_func(db, db->handler_arg, PGS_RESULT_OK, res); } PQclear(res); } /* * Called when select() told that conn is avail for reading. * * It should call postgres handlers and then change state if needed. * * Because the callback may want to close the connection when processing * last resultset, the PGresult handover is delayed one step. */ static void result_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; PGresult *res; db->wait_type = W_NONE; if (!PQconsumeInput(db->con)) { conn_error(db, PGS_RESULT_BAD, "PQconsumeInput"); return; } /* loop until PQgetResult returns NULL */ while (db->con) { /* incomplete result? */ if (PQisBusy(db->con)) { wait_event(db, EV_READ, result_cb); return; } /* next result */ res = PQgetResult(db->con); if (!res) break; report_last_result(db); db->last_result = res; } report_last_result(db); } static void flush(struct PgSocket *db); static void send_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; db->wait_type = W_NONE; flush(db); } /* handle connect states */ static void connect_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; PostgresPollingStatusType poll_res; db->wait_type = W_NONE; poll_res = PQconnectPoll(db->con); switch (poll_res) { case PGRES_POLLING_WRITING: wait_event(db, EV_WRITE, connect_cb); break; case PGRES_POLLING_READING: wait_event(db, EV_READ, connect_cb); break; case PGRES_POLLING_OK: db->connect_time = get_time_usec(); send_event(db, PGS_CONNECT_OK); break; default: conn_error(db, PGS_CONNECT_FAILED, "PQconnectPoll"); } } /* send query to server */ static void flush(struct PgSocket *db) { int res = PQflush(db->con); if (res > 0) { wait_event(db, EV_WRITE, send_cb); } else if (res == 0) { wait_event(db, EV_READ, result_cb); } else conn_error(db, PGS_RESULT_BAD, "PQflush"); } /* override default notice receiver */ static void custom_notice_receiver(void *arg, const PGresult *res) { /* do nothing */ } /* * Public API */ struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *handler_arg) { struct PgSocket *db; db = calloc(1, sizeof(*db)); if (!db) return NULL; db->handler_func = fn; db->handler_arg = handler_arg; db->connstr = strdup(connstr); if (!db->connstr) { pgs_free(db); return NULL; } return db; } void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base) { pgs->base = base; } void pgs_set_lifetime(struct PgSocket *pgs, double lifetime) { pgs->lifetime = USEC * lifetime; } void pgs_connect(struct PgSocket *db) { if (db->con) pgs_disconnect(db); db->con = PQconnectStart(db->connstr); if (db->con == NULL) { conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart"); return; } if (PQstatus(db->con) == CONNECTION_BAD) { conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart"); return; } PQsetNoticeReceiver(db->con, custom_notice_receiver, db); wait_event(db, EV_WRITE, connect_cb); } void pgs_disconnect(struct PgSocket *db) { if (db->wait_type) { event_del(&db->ev); db->wait_type = W_NONE; db->reconnect = false; } if (db->con) { PQfinish(db->con); db->con = NULL; } if (db->last_result) { PQclear(db->last_result); db->last_result = NULL; } } void pgs_free(struct PgSocket *db) { if (db) { pgs_disconnect(db); free(db->connstr); free(db); } } void pgs_sleep(struct PgSocket *db, double timeout) { struct timeval tv; Assert(!db->wait_type); if (db->con && db->lifetime) { usec_t now = get_time_usec(); if (db->connect_time + db->lifetime < now) { pgs_disconnect(db); db->reconnect = true; } } tv.tv_sec = timeout; tv.tv_usec = (timeout - tv.tv_sec) * USEC; evtimer_set(&db->ev, timeout_cb, db); if (db->base) event_base_set(db->base, &db->ev); if (evtimer_add(&db->ev, &tv) < 0) fatal_perror("event_add"); db->wait_type = W_TIME; } void pgs_reconnect(struct PgSocket *db, double timeout) { pgs_disconnect(db); pgs_sleep(db, timeout); db->reconnect = true; } void pgs_send_query_simple(struct PgSocket *db, const char *q) { int res; log_noise("%s", q); res = PQsendQuery(db->con, q); if (!res) { conn_error(db, PGS_RESULT_BAD, "PQsendQuery"); return; } flush(db); } void pgs_send_query_params(struct PgSocket *db, const char *q, int cnt, ...) { int i; va_list ap; const char * args[MAX_QRY_ARGS]; if (cnt < 0 || cnt > MAX_QRY_ARGS) { log_warning("bad query arg cnt"); send_event(db, PGS_RESULT_BAD); return; } va_start(ap, cnt); for (i = 0; i < cnt; i++) args[i] = va_arg(ap, char *); va_end(ap); pgs_send_query_params_list(db, q, cnt, args); } void pgs_send_query_params_list(struct PgSocket *db, const char *q, int cnt, const char *args[]) { int res; log_noise("%s", q); res = PQsendQueryParams(db->con, q, cnt, NULL, args, NULL, NULL, 0); if (!res) { conn_error(db, PGS_RESULT_BAD, "PQsendQueryParams"); return; } flush(db); } int pgs_connection_valid(struct PgSocket *db) { return (db->con != NULL); } PGconn *pgs_get_connection(struct PgSocket *db) { return db->con; } bool pgs_waiting_for_reply(struct PgSocket *db) { if (!db->con) return false; if (PQstatus(db->con) != CONNECTION_OK) return false; return (db->wait_type == W_SOCK) && (db->wait_event == EV_READ); } pgqd/lib/usual/socket_ntop.c0000664000401600040160000001235613175113172014456 0ustar cbecbe/* $OpenBSD: inet_ntop.c,v 1.8 2008/12/09 19:38:38 otto Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM 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 #ifndef HAVE_INET_NTOP #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ 2 #endif #define u_char uint8_t #define u_int unsigned int /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static const char *inet_ntop4(const u_char *src, char *dst, int size); static const char *inet_ntop6(const u_char *src, char *dst, int size); /* char * * inet_ntop(af, src, dst, size) * convert a network format address to presentation format. * return: * pointer to presentation format address (`dst'), or NULL (see errno). * author: * Paul Vixie, 1996. */ const char * inet_ntop(int af, const void *src, char *dst, int size) { if (size < 0) { errno = ENOSPC; return NULL; } switch (af) { case AF_INET: return (inet_ntop4(src, dst, size)); case AF_INET6: return (inet_ntop6(src, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); } /* NOTREACHED */ } /* const char * * inet_ntop4(src, dst, size) * format an IPv4 address, more or less like inet_ntoa() * return: * `dst' (as a const) * notes: * (1) uses no statics * (2) takes a u_char* not an in_addr as input * author: * Paul Vixie, 1996. */ static const char * inet_ntop4(const u_char *src, char *dst, int size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[sizeof "255.255.255.255"]; int l; l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); if (l <= 0 || l >= size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } /* const char * * inet_ntop6(src, dst, size) * convert IPv6 binary address into presentation (printable) format * author: * Paul Vixie, 1996. */ static const char * inet_ntop6(const u_char *src, char *dst, int size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; char *tp, *ep; struct { int base, len; } best, cur; u_int words[IN6ADDRSZ / INT16SZ]; int i; int advance; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = best.len = -1; cur.base = cur.len = -1; for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; ep = tmp + sizeof(tmp); for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) return (NULL); tp += strlen(tp); break; } advance = snprintf(tp, ep - tp, "%x", words[i]); if (advance <= 0 || advance >= ep - tp) return (NULL); tp += advance; } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } if (tp + 1 >= ep) return (NULL); *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((tp - tmp) > size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } #endif pgqd/lib/usual/cfparser.h0000664000401600040160000001413013175113172013730 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Config file parser. */ #ifndef _USUAL_CFPARSER_H_ #define _USUAL_CFPARSER_H_ #include /** * @name Simple line-by-line parser * @{ */ /** Callback signarure for @ref parse_ini_file() */ typedef bool (*cf_handler_f)(void *arg, bool is_sect, const char *key, const char *val); /** * Simple parser, launches callback for each line */ bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg) _MUSTCHECK; /* @} */ /** * @name Complex parser with variable setting. * @{ */ /** @name Per-key flags * @{ */ /** The pointer is absolute */ #define CF_VAL_ABS 0 /** The pointer is relative to base */ #define CF_VAL_REL (1<<1) /** Value must not be changed on reload */ #define CF_NO_RELOAD (1<<2) /** Value can only be read */ #define CF_READONLY (1<<3) /** @} */ /** * Helper structure for passing key info to CfOps */ struct CfValue { void *value_p; const void *extra; const char *key_name; char *buf; int buflen; }; /** * Callbacks for setting and getting a variable value. * * Getter requires temp buf, returns string pointer, which * may or may not point to temp buf. Getter is optional. */ struct CfOps { bool (*setter)(struct CfValue *cv, const char *value); const char *(*getter)(struct CfValue *cv); const void *op_extra; }; /** * Parameter description */ struct CfKey { /** Parameter name */ const char *key_name; /** Type-specific functions, called with absolute pointer */ struct CfOps op; /** Flags: CF_VAL_ABS, CF_VAL_REL */ int flags; /** Absolute or relative offset */ uintptr_t key_ofs; /** Default value as string */ const char *def_value; }; /** * Section description */ struct CfSect { /** Section name */ const char *sect_name; /** Key list */ const struct CfKey *key_list; /** Get base pointer to dynamic sections (optional) */ void *(*base_lookup)(void *top_base, const char *sect_name); /** Set dynamic keys (optional) */ bool (*set_key)(void *base, const char *key, const char *val); /** Get dynamic keys (optional) */ const char *(*get_key)(void *base, const char *key, char *buf, int buflen); /** New section callback (optional) */ bool (*section_start)(void *top_base, const char *sect_name); }; /** * Top-level config information */ struct CfContext { /** Section list */ const struct CfSect *sect_list; /** Top-level base pointer, needed for relative addressing */ void *base; /** If set, then CF_NO_RELOAD keys cannot be changed anymore */ bool loaded; }; /** * @name Type-specific helpers * @{ */ /** Setter for string */ bool cf_set_str(struct CfValue *cv, const char *value); /** Setter for filename */ bool cf_set_filename(struct CfValue *cv, const char *value); /** Setter for int */ bool cf_set_int(struct CfValue *cv, const char *value); /** Setter for unsigned int */ bool cf_set_uint(struct CfValue *cv, const char *value); /** Setter for time-usec */ bool cf_set_time_usec(struct CfValue *cv, const char *value); /** Setter for time-double */ bool cf_set_time_double(struct CfValue *cv, const char *value); /** Setter for lookup */ bool cf_set_lookup(struct CfValue *cv, const char *value); /** Getter for string */ const char *cf_get_str(struct CfValue *cv); /** Getter for int */ const char *cf_get_int(struct CfValue *cv); /** Getter for unsigned int */ const char *cf_get_uint(struct CfValue *cv); /** Getter for time-usec */ const char *cf_get_time_usec(struct CfValue *cv); /** Getter for time-double */ const char *cf_get_time_double(struct CfValue *cv); /** Getter for int lookup */ const char *cf_get_lookup(struct CfValue *cv); /** @} */ /** * @name Shortcut CfOps for well-known types * @{ */ /** Ops for string */ #define CF_STR { cf_set_str, cf_get_str } /** Ops for filename */ #define CF_FILE { cf_set_filename, cf_get_str } /** Ops for integer */ #define CF_INT { cf_set_int, cf_get_int } /** Ops for unsigned integer */ #define CF_UINT { cf_set_uint, cf_get_uint } /** Ops for boolean */ #define CF_BOOL { cf_set_int, cf_get_int } /** Ops for time as usec */ #define CF_TIME_USEC { cf_set_time_usec, cf_get_time_usec } /** Ops for time as double */ #define CF_TIME_DOUBLE { cf_set_time_double, cf_get_time_double } /** Ops for lookup, takes table as argument */ #define CF_LOOKUP(t) { cf_set_lookup, cf_get_lookup, t } /** @} */ /** * Lookup entry for CF_LOOKUP table. */ struct CfLookup { const char *name; int value; }; /** * Helper to describe CfKey with absolute addressing */ #define CF_ABS(name, ops, var, flags, def) \ { name, ops, flags | CF_VAL_ABS, (uintptr_t)&(var), def } /** * Helper to describe CfKey with relative addressing. * * Before using it defined CF_REL_BASE to base struct. * * The var should be field in that struct. * * @code * struct Foo { * char *foo_name; * }; * #define CF_REL_BASE struct Foo * ... * CF_REL("name", CF_STR, foo_name, 0, NULL) * ... * #undef CF_REL_BASE * @endcode */ #define CF_REL(name, ops, var, flags, def) \ { name, ops, flags | CF_VAL_REL, offsetof(CF_REL_BASE, var), def } /** * Load config from file. */ bool cf_load_file(const struct CfContext *cf, const char *fn) _MUSTCHECK; /** * Get single value. */ const char *cf_get(const struct CfContext *cf, const char *sect, const char *var, char *buf, int buflen); /** * Set single value. */ bool cf_set(const struct CfContext *cf, const char *sect, const char *var, const char *val); /* @} */ #endif pgqd/lib/usual/dlfcn.h0000664000401600040160000000254213175113172013215 0ustar cbecbe/* * Dynamic library loading. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_DLFCN_H_ #define _USUAL_DLFCN_H_ #ifdef HAVE_DLFCN_H #include #elif defined(_WIN32) #define dlopen(a,b) usual_dlopen(a,b) #define dlsym(a,b) usual_dlsym(a,b) #define dlclose(a) usual_dlclose(a) #define dlerror(...) usual_dlerror(__VA_ARGS__) /* * win32: Minimal dlopen, dlsym, dlclose, dlerror compat. */ #define RTLD_LAZY 1 #define RTLD_NOW 2 void *dlopen(const char *fn, int flag); void *dlsym(void *hptr, const char *fname); int dlclose(void *hptr); const char *dlerror(void); #endif /* _WIN32 */ #endif /* !_USUAL_DLFCN_H_ */ pgqd/lib/usual/utf8.h0000664000401600040160000000375413175113172013023 0ustar cbecbe/** @file * Low-level UTF8 handling. */ /* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_UTF8_H_ #define _USUAL_UTF8_H_ #include /** * Parse Unicode codepoint from UTF8 stream. * * On invalid UTF8 sequence returns negative byte value and * inreases src_p by one. * * @param src_p Location of data pointer. Will be incremented in-place. * @param srcend Pointer to end of data. * @return UNOCODE codepoint or negative byte value on error. */ int utf8_get_char(const char **src_p, const char *srcend); /** * Write Unicode codepoint as UTF8 sequence. * * Skips invalid Unicode values without error. * * @param c Unicode codepoint. * @param dst_p Location of dest pointer, will be increased in-place. * @param dstend Pointer to end of buffer. * @return false if not room, true otherwise. */ bool utf8_put_char(unsigned int c, char **dst_p, const char *dstend); /** Return UTF8 seq length based on unicode codepoint */ int utf8_char_size(unsigned int c); /** Return UTF8 seq length based on first byte */ int utf8_seq_size(unsigned char c); /** Return sequence length if all bytes are valid, 0 otherwise. */ int utf8_validate_seq(const char *src, const char *srcend); bool utf8_validate_string(const char *src, const char *end); #endif pgqd/lib/usual/socket_pton.c0000664000401600040160000001224013175113172014446 0ustar cbecbe/* $OpenBSD: inet_pton.c,v 1.8 2010/05/06 15:47:14 claudio Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM 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 #ifndef HAVE_INET_PTON #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ 2 #endif #define u_char uint8_t #define u_int unsigned int /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, u_char *dst); static int inet_pton6(const char *src, u_char *dst); /* int * inet_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family * 0 if the address wasn't valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * author: * Paul Vixie, 1996. */ int inet_pton(int af, const char *src, void *dst) { switch (af) { case AF_INET: return (inet_pton4(src, dst)); case AF_INET6: return (inet_pton6(src, dst)); default: errno = EAFNOSUPPORT; return (-1); } /* NOTREACHED */ } /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(const char *src, u_char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; u_char tmp[INADDRSZ], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { u_int new = *tp * 10 + (pch - digits); if (new > 255) return (0); if (! saw_digit) { if (++octets > 4) return (0); saw_digit = 1; } *tp = new; } else if (ch == '.' && saw_digit) { if (octets == 4) return (0); *++tp = 0; saw_digit = 0; } else return (0); } if (octets < 4) return (0); memcpy(dst, tmp, INADDRSZ); return (1); } /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * does not touch `dst' unless it's returning 1. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(const char *src, u_char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, saw_xdigit, count_xdigit; u_int val; memset((tp = tmp), '\0', IN6ADDRSZ); endp = tp + IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return (0); curtok = src; saw_xdigit = count_xdigit = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { if (count_xdigit >= 4) return (0); val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return (0); saw_xdigit = 1; count_xdigit++; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return (0); colonp = tp; continue; } else if (*src == '\0') { return (0); } if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; saw_xdigit = 0; count_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { tp += INADDRSZ; saw_xdigit = 0; count_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return (0); } if (saw_xdigit) { if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) return (0); for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return (0); memcpy(dst, tmp, IN6ADDRSZ); return (1); } #endif pgqd/lib/usual/pgutil_kwlookup.g0000664000401600040160000000242513175113172015365 0ustar cbecbe/* gperf header for kwlookup */ %language=ANSI-C %readonly-tables %pic %enum %define lookup-function-name pg_keyword_lookup_real %define hash-function-name pg_keyword_lookup_hash %define string-pool-name pgkw %% all analyse analyze and any array as asc asymmetric authorization between bigint binary bit boolean both case cast char character check coalesce collate column concurrently constraint create cross current_catalog current_date current_role current_schema current_time current_timestamp current_user dec decimal default deferrable desc distinct do else end except exists extract false fetch float for foreign freeze from full grant greatest group having ilike in initially inner inout int integer intersect interval into is isnull join leading least left like limit localtime localtimestamp national natural nchar new none not notnull null nullif numeric off offset old on only or order out outer over overlaps overlay placing position precision primary real references returning right row select session_user setof similar smallint some substring symmetric table then time timestamp to trailing treat trim true union unique user using values varchar variadic verbose when where window with xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize pgqd/lib/usual/ctype.h0000664000401600040160000000575213175113172013261 0ustar cbecbe/* * ctype wrappers * * Copyright (c) 2011 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * ctype compat. * * Provides wrappers that make sure the functions work on 'char' values. * * @note * POSIX requires that these functions accept EOF/-1 in addition * to ordinary byte values. That means when working on 'char', * the functions cannot differetiate between 0xFF and EOF. * As no code should give EOF to functions and no code * should depend whether 0xFF is labeled ispunct() or not, * it seems no worthwhile to fix it. */ #ifndef _USUAL_CTYPE_H_ #define _USUAL_CTYPE_H_ #include #include #ifndef isblank #define isblank usual_isblank static inline int isblank(int c) { return (c == ' ') || (c == '\t'); } #endif /* keep right signature, cast to uchar internally */ #define _WRAP_CTYPE_FN(name) \ static inline int safe_ ## name (int c) { \ return name((unsigned char)(c)); \ } _WRAP_CTYPE_FN(isalnum) #undef isalnum /** Safe isalnum */ #define isalnum safe_isalnum _WRAP_CTYPE_FN(isalpha) #undef isalpha /** Safe isalpha */ #define isalpha safe_isalpha _WRAP_CTYPE_FN(isascii) #undef isascii /** Safe isascii */ #define isascii safe_isascii _WRAP_CTYPE_FN(isblank) #undef isblank /** Safe isblank */ #define isblank safe_isblank _WRAP_CTYPE_FN(iscntrl) #undef iscntrl /** Safe iscntrl */ #define iscntrl safe_iscntrl _WRAP_CTYPE_FN(isdigit) #undef isdigit /** Safe isdigit */ #define isdigit safe_isdigit _WRAP_CTYPE_FN(isgraph) #undef isgraph /** Safe isgraph */ #define isgraph safe_isgraph _WRAP_CTYPE_FN(islower) #undef islower /** Safe islower */ #define islower safe_islower _WRAP_CTYPE_FN(isprint) #undef isprint /** Safe isprint */ #define isprint safe_isprint _WRAP_CTYPE_FN(ispunct) #undef ispunct /** Safe ispunct */ #define ispunct safe_ispunct _WRAP_CTYPE_FN(isspace) #undef isspace /** Safe isspace */ #define isspace safe_isspace _WRAP_CTYPE_FN(isupper) #undef isupper /** Safe isupper */ #define isupper safe_isupper _WRAP_CTYPE_FN(isxdigit) #undef isxdigit /** Safe isxdigit */ #define isxdigit safe_isxdigit _WRAP_CTYPE_FN(tolower) #undef tolower /** Safe tolower */ #define tolower safe_tolower _WRAP_CTYPE_FN(toupper) #undef toupper /** Safe toupper */ #define toupper safe_toupper #undef _WRAP_CTYPE_FN #endif /* _USUAL_CTYPE_H_ */ pgqd/lib/usual/mbuf.c0000664000401600040160000000117413175113172013053 0ustar cbecbe /* * Safe and easy access to memory buffer. */ #include bool mbuf_make_room(struct MBuf *buf, unsigned len) { unsigned new_alloc = buf->alloc_len; void *ptr; /* is it a dynamic buffer */ if (buf->reader || buf->fixed) return false; /* maybe there is enough room already */ if (buf->write_pos + len <= buf->alloc_len) return true; if (new_alloc == 0) new_alloc = 128; /* calc new alloc size */ while (new_alloc < buf->write_pos + len) new_alloc *= 2; /* realloc */ ptr = realloc(buf->data, new_alloc); if (!ptr) return false; buf->data = ptr; buf->alloc_len = new_alloc; return true; } pgqd/lib/usual/getopt.c0000664000401600040160000003245213175113172013427 0ustar cbecbe/* $OpenBSD: getopt_long.c,v 1.24 2010/07/22 19:31:53 blambert Exp $ */ /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ /* * Copyright (c) 2002 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. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #ifdef NEED_USUAL_GETOPT #include #include char *optarg; /* argument associated with option */ int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #define EMSG "" static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too) { char *current_argv, *has_equal; size_t current_argv_len; int i, match; current_argv = place; match = -1; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* partial match */ match = i; else { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; int optreset = 0; if (options == NULL) return (-1); /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. */ if (posixly_correct == -1) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; else if (*options == '-') flags |= FLAG_ALLARGS; if (*options == '+' || *options == '-') options++; /* * reset if requested */ if (optind == 0) optind = optreset = 1; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt -- * Parse argc/argv argument vector. */ int getopt(int nargc, char *nargv[], const char *options) { return getopt_internal(nargc, nargv, options, NULL, NULL, FLAG_PERMUTE); } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char *nargv[], const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char *nargv[], const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } #endif /* NEED_USUAL_GETOPT */ pgqd/lib/usual/cfparser.c0000664000401600040160000003003313175113172013723 0ustar cbecbe/* * Config file parser. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_PWD_H #include #endif #include #include #include #include #include #define MAX_INCLUDE 10 /* * INI file parser. */ static int count_lines(const char *s, const char *end) { int lineno = 1; for (; s < end; s++) { if (*s == '\n') lineno++; } return lineno; } static bool parse_ini_file_internal(const char *fn, cf_handler_f user_handler, void *arg, int inclevel) { char *buf; char *p, *key, *val; int klen, vlen; char o1, o2; bool ok; buf = load_file(fn, NULL); if (buf == NULL) return false; p = buf; while (*p) { /* space at the start of line - including empty lines */ while (*p && isspace(*p)) p++; if (strncmp(p, "%include", 8) == 0 && p[8] != 0 && isblank(p[8])) { if (inclevel >= MAX_INCLUDE) { log_error("include nesting level too deep (%s:%d), stopping loading", fn, count_lines(buf, p)); goto failed; } p += 8; while (*p && isblank(*p)) p++; /* now read value */ val = p; while (*p && (*p != '\n')) p++; vlen = p - val; /* eat space at end */ while (vlen > 0 && isspace(val[vlen - 1])) vlen--; /* * val now has the name of the file to be included. * Process it recursively. */ o1 = val[vlen]; val[vlen] = 0; log_debug("processing include: %s", val); ok = parse_ini_file_internal(val, user_handler, arg, inclevel + 1); val[vlen] = o1; if (!ok) goto failed; log_debug("returned to processing file %s", fn); continue; } /* skip comment lines */ if (*p == '#' || *p == ';') { while (*p && *p != '\n') p++; continue; } /* got new section */ if (*p == '[') { key = ++p; while (*p && *p != ']' && *p != '\n') p++; if (*p != ']') goto syntax_error; o1 = *p; *p = 0; log_debug("parse_ini_file: [%s]", key); ok = user_handler(arg, true, key, NULL); *p++ = o1; if (!ok) goto failed; continue; } /* done? */ if (*p == 0) break; /* read key val */ key = p; while (*p && (isalnum(*p) || strchr("_.-*", *p))) p++; klen = p - key; /* expect '=', skip it */ while (*p && (*p == ' ' || *p == '\t')) p++; if (*p != '=') { goto syntax_error; } else p++; while (*p && (*p == ' ' || *p == '\t')) p++; /* now read value */ val = p; while (*p && (*p != '\n')) p++; vlen = p - val; /* eat space at end */ while (vlen > 0 && isspace(val[vlen - 1])) vlen--; /* skip junk */ while (*p && isspace(*p)) p++; /* our buf is r/w, so take it easy */ o1 = key[klen]; o2 = val[vlen]; key[klen] = 0; val[vlen] = 0; log_debug("parse_ini_file: '%s' = '%s'", key, val); ok = user_handler(arg, false, key, val); log_debug("parse_ini_file: '%s' = '%s' ok:%d", key, val, ok); /* restore data, to keep count_lines() working */ key[klen] = o1; val[vlen] = o2; if (!ok) goto failed; } free(buf); return true; syntax_error: log_error("syntax error in configuration (%s:%d), stopping loading", fn, count_lines(buf, p)); failed: free(buf); return false; } bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg) { return parse_ini_file_internal(fn, user_handler, arg, 0); } /* * Config framework. */ static void *get_dest(void *base, const struct CfKey *k) { char *dst; if (k->flags & CF_VAL_REL) { /* relative address requires base */ if (!base) return NULL; dst = (char *)base + k->key_ofs; } else dst = (char *)k->key_ofs; return dst; } static const struct CfSect *find_sect(const struct CfContext *cf, const char *name) { const struct CfSect *s; for (s = cf->sect_list; s->sect_name; s++) { if (strcmp(s->sect_name, name) == 0) return s; if (strcmp(s->sect_name, "*") == 0) return s; } return NULL; } static const struct CfKey *find_key(const struct CfSect *s, const char *key) { const struct CfKey *k; for (k = s->key_list; k->key_name; k++) { if (strcmp(k->key_name, key) == 0) return k; } return k; } const char *cf_get(const struct CfContext *cf, const char *sect, const char *key, char *buf, int buflen) { const struct CfSect *s; const struct CfKey *k; void *base, *p; struct CfValue cv; /* find section */ s = find_sect(cf, sect); if (!s) return NULL; /* find section base */ base = cf->base; if (s->base_lookup) base = s->base_lookup(base, sect); /* handle dynamic keys */ if (s->set_key) { if (!s->get_key) return NULL; return s->get_key(base, key, buf, buflen); } /* get fixed key */ k = find_key(s, key); if (!k || !k->op.getter) return NULL; p = get_dest(base, k); if (!p) return NULL; cv.key_name = k->key_name; cv.extra = k->op.op_extra; cv.value_p = p; cv.buf = buf; cv.buflen = buflen; return k->op.getter(&cv); } bool cf_set(const struct CfContext *cf, const char *sect, const char *key, const char *val) { const struct CfSect *s; const struct CfKey *k; void *base, *p; struct CfValue cv; /* find section */ s = find_sect(cf, sect); if (!s) { log_error("Unknown section: %s", sect); return false; } /* find section base */ base = cf->base; if (s->base_lookup) base = s->base_lookup(base, sect); /* handle dynamic keys */ if (s->set_key) return s->set_key(base, key, val); /* set fixed key */ k = find_key(s, key); if (!k) { log_error("Unknown parameter: %s/%s", sect, key); return false; } if (!k->op.setter || (k->flags & CF_READONLY)) { /* silently ignore */ return true; } if ((k->flags & CF_NO_RELOAD) && cf->loaded) { /* silently ignore */ return true; } p = get_dest(base, k); if (!p) { log_error("Bug - no base for relative key: %s/%s", sect, key); return false; } cv.key_name = k->key_name; cv.extra = k->op.op_extra; cv.value_p = p; cv.buf = NULL; cv.buflen = 0; return k->op.setter(&cv, val); } /* * File loader */ struct LoaderCtx { const struct CfContext *cf; const char *cur_sect; void *top_base; bool got_main_sect; }; static bool fill_defaults(struct LoaderCtx *ctx) { const struct CfKey *k; const struct CfSect *s; s = find_sect(ctx->cf, ctx->cur_sect); if (!s) goto fail; if (s == ctx->cf->sect_list) ctx->got_main_sect = true; if (s->section_start) { if (!s->section_start(ctx->top_base, ctx->cur_sect)) return false; } if (s->set_key) return true; for (k = s->key_list; k->key_name; k++) { if (!k->def_value || (k->flags & CF_READONLY)) continue; if ((k->flags & CF_NO_RELOAD) && ctx->cf->loaded) continue; if (!cf_set(ctx->cf, ctx->cur_sect, k->key_name, k->def_value)) goto fail; } return true; fail: log_error("fill_defaults fail"); return false; } static bool load_handler(void *arg, bool is_sect, const char *key, const char *val) { struct LoaderCtx *ctx = arg; if (is_sect) { if (ctx->cur_sect) free(ctx->cur_sect); ctx->cur_sect = strdup(key); if (!ctx->cur_sect) return false; return fill_defaults(ctx); } else if (!ctx->cur_sect) { log_error("load_init_file: value without section: %s", key); return false; } else { return cf_set(ctx->cf, ctx->cur_sect, key, val); } } bool cf_load_file(const struct CfContext *cf, const char *fn) { struct LoaderCtx ctx; bool ok; memset(&ctx, 0, sizeof(ctx)); ctx.cf = cf; ok = parse_ini_file(fn, load_handler, &ctx); if (ctx.cur_sect) free(ctx.cur_sect); if (ok && !ctx.got_main_sect) { log_error("load_init_file: main section missing from config file"); return false; } return ok; } /* * Various value parsers. */ bool cf_set_int(struct CfValue *cv, const char *value) { int *ptr = cv->value_p; char *end; long val; errno = 0; val = strtol(value, &end, 0); if (end == value || *end != 0) { /* reject partial parse */ if (!errno) errno = EINVAL; return false; } *ptr = val; return true; } bool cf_set_uint(struct CfValue *cv, const char *value) { unsigned int *ptr = cv->value_p; char *end; unsigned long val; errno = 0; val = strtoul(value, &end, 0); if (end == value || *end != 0) { /* reject partial parse */ if (!errno) errno = EINVAL; return false; } *ptr = val; return true; } bool cf_set_str(struct CfValue *cv, const char *value) { char **dst_p = cv->value_p; char *tmp = strdup(value); if (!tmp) { log_error("cf_set_str: no mem"); return false; } if (*dst_p) free(*dst_p); *dst_p = tmp; return true; } bool cf_set_filename(struct CfValue *cv, const char *value) { char **dst_p = cv->value_p; char *tmp, *home, *p; int v_len, usr_len, home_len; struct passwd *pw; /* do we need to do tilde expansion */ if (value[0] != '~') return cf_set_str(cv, value); /* find username end */ v_len = strlen(value); if ((p = memchr(value, '/', v_len)) == NULL) usr_len = v_len - 1; else usr_len = (p - value) - 1; if (usr_len) { p = malloc(usr_len + 1); if (!p) return false; memcpy(p, value + 1, usr_len); p[usr_len] = 0; pw = getpwnam(p); free(p); if (!pw) goto fail; home = pw->pw_dir; } else { home = getenv("HOME"); if (!home) { pw = getpwuid(getuid()); if (!pw) goto fail; home = pw->pw_dir; } } if (!home) goto fail; home_len = strlen(home); tmp = malloc(v_len - usr_len + home_len); if (!tmp) return false; memcpy(tmp, home, home_len); memcpy(tmp + home_len, value + usr_len + 1, v_len - usr_len - 1); tmp[v_len - 1 - usr_len + home_len] = 0; log_debug("expanded '%s' -> '%s'", value, tmp); if (*dst_p) free(*dst_p); *dst_p = tmp; return true; fail: log_error("cannot to expand filename: %s", value); return false; } /* parse float with error checking. returns -1 if failed */ static double parse_time(const char *value) { double v; char *endp = NULL; errno = 0; v = strtod_dot(value, &endp); if (errno) return -1; if (*endp || endp == value || v < 0) { errno = EINVAL; return -1; } return v; } bool cf_set_time_usec(struct CfValue *cv, const char *value) { usec_t *ptr = cv->value_p; double v = parse_time(value); if (v < 0) return false; *ptr = (usec_t)(USEC * v); return true; } bool cf_set_time_double(struct CfValue *cv, const char *value) { double *ptr = cv->value_p; double v = parse_time(value); if (v < 0) return false; *ptr = v; return true; } /* * Various value formatters. */ const char *cf_get_str(struct CfValue *cv) { char **p = cv->value_p; return *p; } const char *cf_get_int(struct CfValue *cv) { int *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%d", *p); return cv->buf; } const char *cf_get_uint(struct CfValue *cv) { unsigned int *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%u", *p); return cv->buf; } const char *cf_get_time_double(struct CfValue *cv) { double *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%g", *p); return cv->buf; } const char *cf_get_time_usec(struct CfValue *cv) { struct CfValue tmp = *cv; usec_t *p = cv->value_p; double d = (double)(*p) / USEC; tmp.value_p = &d; return cf_get_time_double(&tmp); } /* * str->int mapping */ const char *cf_get_lookup(struct CfValue *cv) { int *p = cv->value_p; const struct CfLookup *lk = cv->extra; for (; lk->name; lk++) { if (lk->value == *p) return lk->name; } return "INVALID"; } bool cf_set_lookup(struct CfValue *cv, const char *value) { int *p = cv->value_p; const struct CfLookup *lk = cv->extra; for (; lk->name; lk++) { if (strcasecmp(lk->name, value) == 0) { *p = lk->value; return true; } } return false; } pgqd/lib/usual/utf8.c0000664000401600040160000001162713175113172013014 0ustar cbecbe/* * Low-level UTF8 handling. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #define u8head(c, mask) (((c) & (mask | (mask >> 1))) == mask) #define u8tail(c) u8head(c, 0x80) /* * conservative utf8 decoder * * if invalid char, advance src pointer by one and return * negative byte value. this can be ignored or replaced. */ int utf8_get_char(const char **src_p, const char *_srcend) { uint32_t c; const uint8_t *srcend = (uint8_t *)_srcend; const uint8_t *p = (uint8_t *)(*src_p); /* * 0xxx xxxx -> len=1 * 10xx xxxx -> tail byte * 110x xxxx -> len=2 * 1110 xxxx -> len=3 * 1111 0xxx -> len=4 */ if (p[0] < 0x80) { c = *p++; } else if (u8head(p[0], 0xC0)) { if (p + 2 > srcend) goto eos; if (!u8tail(p[1])) goto bad_enc; c = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); if (c < 0x80) goto bad_enc; p += 2; } else if (u8head(p[0], 0xE0)) { if (p + 3 > srcend) goto eos; if (!u8tail(p[1]) || !u8tail(p[2])) goto bad_enc; c = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); if ((c < 0x800) || ((c & 0xF800) == 0xD800)) goto bad_enc; p += 3; } else if (u8head(p[0], 0xF0)) { if (p + 4 > srcend) goto eos; if (!u8tail(p[1]) || !u8tail(p[2]) || !u8tail(p[3])) goto bad_enc; c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); if (c < 0x10000 || c > 0x10FFFF) goto bad_enc; p += 4; } else { goto bad_enc; } *src_p = (char *)p; return c; bad_enc: eos: c = p[0]; *src_p = (char *)p + 1; return -(int)c; } /* encode one char - skip invalid ones */ bool utf8_put_char(unsigned int c, char **dst_p, const char *dstend) { char *dst = *dst_p; if (c < 0x80) { if (dst + 1 > dstend) goto no_room; *dst++ = c; } else if (c < 0x800) { if (dst + 2 > dstend) goto no_room; *dst++ = 0xC0 | (c >> 6); *dst++ = 0x80 | (c & 0x3F); } else if (c < 0x10000) { if (dst + 3 > dstend) goto no_room; if (c < 0xD800 || c > 0xDFFF) { *dst++ = 0xE0 | (c >> 12); *dst++ = 0x80 | ((c >> 6) & 0x3F); *dst++ = 0x80 | (c & 0x3F); } } else if (c <= 0x10FFFF) { if (dst + 4 > dstend) goto no_room; *dst++ = 0xF0 | (c >> 18); *dst++ = 0x80 | ((c >> 12) & 0x3F); *dst++ = 0x80 | ((c >> 6) & 0x3F); *dst++ = 0x80 | (c & 0x3F); } *dst_p = dst; return true; no_room: return false; } int utf8_char_size(unsigned int c) { if (c < 0x80) return 1; if (c < 0x800) return 2; if (c < 0x10000) return 3; return 4; } int utf8_seq_size(unsigned char b) { if (b < 0x80) return 1; if (b < 0xC2) return 0; if (b < 0xE0) return 2; if (b < 0xF0) return 3; if (b < 0xF5) return 4; return 0; } /* * 7f: c1bf (+1) * 80: c280 * 7ff: dfbf * 7ff: e09fbf (+1) * 800: e0a080 * ffff: efbfbf * ffff: f08fbfbf (+1) * 10000: f0908080 * 10ffff: f48fbfbf */ int utf8_validate_seq(const char *src, const char *srcend) { const unsigned char *u = (unsigned char *)src; const unsigned char *uend = (unsigned char *)srcend; if (u[0] < 0x80) { /* ascii */ if (u[0] == 0) goto invalid; return 1; } else if (u[0] < 0xC2) { /* tail byte as first byte */ goto invalid; } else if (u[0] < 0xE0) { /* 1 tail byte */ if (u + 2 > uend) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; return 2; } else if (u[0] < 0xF0) { /* 2 tail bytes */ if (u + 3 > uend) goto invalid; if (u[0] == 0xE0 && u[1] < 0xA0) goto invalid; if (u[0] == 0xED && u[1] >= 0xA0) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; if ((u[2] & 0xC0) != 0x80) goto invalid; return 3; } else if (u[0] < 0xF5) { /* 3-tail bytes */ if (u + 4 > uend) goto invalid; if (u[0] == 0xF0 && u[1] < 0x90) goto invalid; if (u[0] == 0xF4 && u[1] > 0x8F) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; if ((u[2] & 0xC0) != 0x80) goto invalid; if ((u[3] & 0xC0) != 0x80) goto invalid; return 4; } invalid: return 0; } bool utf8_validate_string(const char *src, const char *end) { unsigned int n; while (src < end) { if (*src & 0x80) { n = utf8_validate_seq(src, end); if (n == 0) return false; src += n; } else if (*src == '\0') { return false; } else { src++; } } return true; } pgqd/lib/usual/socket.c0000664000401600040160000002504013175113172013410 0ustar cbecbe/* * Socket helpers and compat. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifdef HAVE_UCRED_H #include #endif #ifdef HAVE_SYS_UCRED_H #include #endif /* toggle non-blocking flag */ bool socket_set_nonblocking(int fd, bool non_block) { int flags; /* get old flags */ flags = fcntl(fd, F_GETFL, 0); if (flags < 0) return false; /* flip O_NONBLOCK */ if (non_block) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; /* set new flags */ if (fcntl(fd, F_SETFL, flags) < 0) return false; return true; } /* initial socket setup */ bool socket_setup(int sock, bool non_block) { int res; #ifdef SO_NOSIGPIPE /* disallow SIGPIPE, if possible */ int val = 1; res = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)); if (res < 0) return false; #endif /* close fd on exec */ res = fcntl(sock, F_SETFD, FD_CLOEXEC); if (res < 0) return false; /* when no data available, return EAGAIN instead blocking */ if (!socket_set_nonblocking(sock, non_block)) return false; return true; } bool socket_set_keepalive(int fd, int onoff, int keepidle, int keepintvl, int keepcnt) { int val, res; if (!onoff) { /* turn keepalive off */ val = 0; res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); return (res == 0); } /* turn keepalive on */ val = 1; res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); if (res < 0) return false; /* Darwin */ #ifdef TCP_KEEPALIVE if (keepidle) { val = keepidle; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif /* Linux, NetBSD */ #ifdef TCP_KEEPIDLE if (keepidle) { val = keepidle; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif #ifdef TCP_KEEPINTVL if (keepintvl) { val = keepintvl; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif #ifdef TCP_KEEPCNT if (keepcnt > 0) { val = keepcnt; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif /* Windows */ #ifdef SIO_KEEPALIVE_VALS if (keepidle || keepintvl) { struct tcp_keepalive vals; DWORD outlen = 0; if (!keepidle) keepidle = 5 * 60; if (!keepintvl) keepintvl = 15; vals.onoff = 1; vals.keepalivetime = keepidle * 1000; vals.keepaliveinterval = keepintvl * 1000; res = WSAIoctl(fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0, &outlen, NULL, NULL); if (res != 0) return false; } #endif return true; } /* * Convert sockaddr to string. Supports ipv4, ipv6 and unix sockets. */ const char *sa2str(const struct sockaddr *sa, char *dst, int dstlen) { const struct sockaddr_in *in; const struct sockaddr_in6 *in6; const struct sockaddr_un *un; const char *tmp; char buf[128]; switch (sa->sa_family) { case AF_INET: in = (struct sockaddr_in *)sa; tmp = inet_ntop(AF_INET, &in->sin_addr, buf, sizeof(buf)); if (!tmp) return NULL; snprintf(dst, dstlen, "%s:%d", tmp, ntohs(in->sin_port)); break; case AF_INET6: in6 = (struct sockaddr_in6 *)sa; tmp = inet_ntop(AF_INET6, &in6->sin6_addr, buf, sizeof(buf)); if (!tmp) return NULL; snprintf(dst, dstlen, "%s/%d", tmp, ntohs(in6->sin6_port)); break; case AF_UNIX: un = (struct sockaddr_un *)sa; snprintf(dst, dstlen, "unix:%s", un->sun_path); break; default: snprintf(dst, dstlen, "sa2str(%d): unknown proto", sa->sa_family); break; } return dst; } #ifndef HAVE_GETPEEREID /* * Get other side's uid and gid for UNIX socket. */ int getpeereid(int fd, uid_t *uid_p, gid_t *gid_p) { pid_t pid; return getpeercreds(fd, uid_p, gid_p, &pid); } #endif /* * Get uid, gid and pid of unix socket peer. * * Pid may not be availalbe on some OSes. * It's set to 0 then. */ int getpeercreds(int fd, uid_t *uid_p, gid_t *gid_p, pid_t *pid_p) { /* What a mess */ #if defined(SO_PEERCRED) /* linux and others */ #if defined(HAVE_SYS_UCRED_H) struct sockpeercred cred; /* openbsd */ #else struct ucred cred; /* linux */ #endif socklen_t len = sizeof(cred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0) { *uid_p = cred.uid; *gid_p = cred.gid; *pid_p = cred.pid; return 0; } return -1; #elif defined(HAVE_GETPEERUCRED) /* solaris */ ucred_t *cred = NULL; if (getpeerucred(fd, &cred) == 0) { *uid_p = ucred_geteuid(cred); *gid_p = ucred_getegid(cred); *pid_p = ucred_getpid(cred); ucred_free(cred); if ((int)*uid_p == -1 || (int)*gid_p == -1) return -1; return 0; } return -1; #elif defined(LOCAL_PEEREID) /* netbsd */ struct unpcbid cred; socklen_t len = sizeof(cred); if (getsockopt(fd, 0, LOCAL_PEEREID, &cred, &len) != 0) return -1; *uid_p = cred.unp_euid; *gid_p = cred.unp_egid; *pid_p = cred.unp_pid; return 0; #elif defined(HAVE_GETPEEREID) /* generic bsd; no pid */ *pid_p = 0; return getpeereid(fd, uid_p, gid_p) == 0 ? 0 : -1; #elif defined(LOCAL_PEERCRED) /* freebsd, osx, dfly; no pid */ struct xucred cred; socklen_t len = sizeof(cred); if (getsockopt(fd, 0, LOCAL_PEERCRED, &cred, &len) != 0) return -1; if (cred.cr_version != XUCRED_VERSION) { errno = EINVAL; return -1; } *uid_p = cred.cr_uid; *gid_p = cred.cr_gid; *pid_p = 0; return 0; #else /* no implementation */ errno = ENOSYS; return -1; #endif } #ifndef HAVE_POLL /* * Emulate poll() with select() */ #ifdef HAVE_SYS_SELECT_H #include #endif /* * dynamic buffer for fd_set to avoid depending on FD_SETSIZE */ struct fd_buf { fd_set *set; int alloc_bytes; }; static void fdbuf_zero(struct fd_buf *buf) { if (buf->set) memset(buf->set, 0, buf->alloc_bytes); } static bool fdbuf_resize(struct fd_buf *buf, int fd) { int need_bytes; unsigned char *ptr; /* default allocation */ int alloc = sizeof(fd_set); #ifdef WIN32 int cnt = buf->set ? buf->set->fd_count : 0; /* win32 fd_set is array of handles, +8 for count&padding */ need_bytes = (cnt + 1) * sizeof(buf->set->fd_array[0]) + 8; #else /* otherwise, fd_set is bitmap, +8 for int/long alignment */ need_bytes = fd / 8 + 8; #endif if (buf->alloc_bytes < need_bytes) { while (alloc < need_bytes) alloc *= 2; if (!buf->set) ptr = malloc(alloc); else ptr = realloc(buf->set, alloc); if (!ptr) return false; /* clean new area */ memset(ptr + buf->alloc_bytes, 0, alloc - buf->alloc_bytes); buf->set = (fd_set *)ptr; buf->alloc_bytes = alloc; } return true; } /* win32: make macros ignore FD_SETSIZE */ #undef FD_SETSIZE #define FD_SETSIZE (1 << 30) int poll(struct pollfd *fds, nfds_t nfds, int timeout_ms) { static struct fd_buf readfds = { NULL, 0 }; static struct fd_buf writefds = { NULL, 0 }; struct pollfd *pf; int res, fd_max = 0; struct timeval *tv = NULL; struct timeval tvreal; unsigned i; /* convert timeout_ms to timeval */ if (timeout_ms >= 0) { tvreal.tv_sec = timeout_ms / 1000; tvreal.tv_usec = (timeout_ms % 1000) * 1000; tv = &tvreal; } else if (timeout_ms < -1) goto err_inval; /* * Convert pollfds to fd sets. */ fdbuf_zero(&readfds); fdbuf_zero(&writefds); for (i = 0; i < nfds; i++) { pf = fds + i; if (pf->fd < 0) goto err_badf; /* sets must be equal size */ if (!fdbuf_resize(&readfds, pf->fd)) goto err_nomem; if (!fdbuf_resize(&writefds, pf->fd)) goto err_nomem; if (pf->events & POLLIN) FD_SET((unsigned)pf->fd, readfds.set); if (pf->events & POLLOUT) FD_SET((unsigned)pf->fd, writefds.set); if (pf->fd > fd_max) fd_max = pf->fd; } res = select(fd_max + 1, readfds.set, writefds.set, NULL, tv); if (res <= 0) return res; /* * select() and poll() count fd-s differently, * need to recount them here. */ res = 0; for (i = 0; i < nfds; i++) { pf = fds + i; pf->revents = 0; if ((pf->events & POLLIN) && FD_ISSET(pf->fd, readfds.set)) pf->revents |= POLLIN; if ((pf->events & POLLOUT) && FD_ISSET(pf->fd, writefds.set)) pf->revents |= POLLOUT; if (pf->revents) res += 1; } return res; err_nomem: errno = ENOMEM; return -1; err_badf: errno = EBADF; return -1; err_inval: errno = EINVAL; return -1; } #endif /* PLPROXY_POLL_COMPAT */ #ifdef WIN32 /* create local TCP socket, idea from libevent/Tor */ int win32_socketpair(int d, int typ, int proto, int sv[2]) { int list = -1, s1 = -1, s2 = -1; struct sockaddr_in sa1, sa2; socklen_t slen = sizeof(sa1); int res; if (d != AF_INET && d != AF_UNIX) goto err_inval; if (proto || !sv) goto err_inval; /* prepare sockaddr for bind */ memset(&sa1, 0, sizeof(sa1)); sa1.sin_family = AF_INET; sa1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa1.sin_port = htons(0); /* create listen socket */ list = socket(AF_INET, typ, 0); if (list == -1) return -1; res = bind(list, (struct sockaddr *)&sa1, sizeof(sa1)); if (res == -1) goto failed; res = listen(list, 1); if (res == -1) goto failed; /* read listen port */ res = getsockname(list, (struct sockaddr *)&sa1, &slen); if (res == -1 || slen != sizeof(sa1)) goto failed; /* connect to it */ s1 = socket(AF_INET, typ, 0); if (s1 == -1) goto failed; res = connect(s1, (struct sockaddr *)&sa1, sizeof(sa1)); if (res == -1) goto failed; /* and accept from other end */ s2 = accept(list, (struct sockaddr *)&sa2, &slen); if (s2 == -1 || slen != sizeof(sa2)) goto failed; /* sanity check */ res = getsockname(s1, (struct sockaddr *)&sa1, &slen); if (res == -1 || slen != sizeof(sa1)) goto failed; if (sa1.sin_port != sa2.sin_port) goto failed; closesocket(list); sv[0] = s1; sv[1] = s2; return 0; failed: errno = (res == -1) ? WSAGetLastError() : EFAULT; if (list != -1) closesocket(list); if (s1 != -1) closesocket(s1); if (s2 != -1) closesocket(s2); return -1; err_inval: errno = EINVAL; return -1; } #endif pgqd/lib/usual/mdict.h0000664000401600040160000000555713175113172013240 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Minimal dict. */ #ifndef _USUAL_MDICT_H_ #define _USUAL_MDICT_H_ #include #include /** Dict reference */ struct MDict; /** Create new emtpy dict */ struct MDict *mdict_new(CxMem *cx); /** Free dict */ void mdict_free(struct MDict *dict); /** Get value as MBuf from string */ const struct MBuf *mdict_get_buf(struct MDict *dict, const char *key, unsigned klen); /** Get value from dict */ const char *mdict_get_str(struct MDict *dict, const char *key, unsigned klen); /** Put string to dict */ bool mdict_put_str(struct MDict *dict, const char *key, unsigned klen, const char *val, unsigned vlen); /** Remove a key from dict */ bool mdict_del_key(struct MDict *dict, const char *key, unsigned klen); /** Signature for walker callback */ typedef bool (*mdict_walker_f)(void *arg, const struct MBuf *k, const struct MBuf *v); /** Walk over dict */ bool mdict_walk(struct MDict *dict, mdict_walker_f cb_func, void *cb_arg); /* * Simple API that calculates strlen inline. */ /** Get value from dict */ static inline const char *mdict_get(struct MDict *dict, const char *key) { return mdict_get_str(dict, key, strlen(key)); } /** Put zero-terminated key and value to dict */ static inline bool mdict_put(struct MDict *dict, const char *key, const char *val) { unsigned klen = strlen(key); unsigned vlen = val ? strlen(val) : 0; return mdict_put_str(dict, key, klen, val, vlen); } /** Put MBuf to dict */ static inline bool mdict_put_buf(struct MDict *dict, const char *key, const struct MBuf *buf) { unsigned klen = strlen(key); const char *val = buf ? mbuf_data(buf) : NULL; unsigned vlen = buf ? mbuf_written(buf) : 0; return mdict_put_str(dict, key, klen, val, vlen); } /** Remove value from dict */ static inline bool mdict_del(struct MDict *dict, const char *key) { return mdict_del_key(dict, key, strlen(key)); } /** Urldecode string and add keys with values to dict */ bool mdict_urldecode(struct MDict *dict, const char *str, unsigned len); /** Urlencode dict to string */ bool mdict_urlencode(struct MDict *dict, struct MBuf *dst); #endif pgqd/lib/usual/time.c0000664000401600040160000001117613175113172013063 0ustar cbecbe/* * Common time functions. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include char *format_time_ms(usec_t time, char *dst, unsigned dstlen) { struct tm *tm, tmbuf; struct timeval tv; time_t sec; if (!time) { gettimeofday(&tv, NULL); } else { tv.tv_sec = time / USEC; tv.tv_usec = time % USEC; } sec = tv.tv_sec; tm = localtime_r(&sec, &tmbuf); snprintf(dst, dstlen, "%04d-%02d-%02d %02d:%02d:%02d.%03d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000)); return dst; } char *format_time_s(usec_t time, char *dst, unsigned dstlen) { time_t s; struct tm tbuf, *tm; if (!time) { struct timeval tv; gettimeofday(&tv, NULL); s = tv.tv_sec; } else { s = time / USEC; } tm = localtime_r(&s, &tbuf); snprintf(dst, dstlen, "%04d-%02d-%02d %02d:%02d:%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); return dst; } /* read current time */ usec_t get_time_usec(void) { struct timeval tv; gettimeofday(&tv, NULL); return (usec_t)tv.tv_sec * USEC + tv.tv_usec; } static usec_t _time_cache; /* read cached time */ usec_t get_cached_time(void) { if (!_time_cache) _time_cache = get_time_usec(); return _time_cache; } /* forget cached time, let next read fill it */ void reset_time_cache(void) { _time_cache = 0; } /* * win32 compat */ #ifdef WIN32 /* unix epoch (1970) in seconds from windows epoch (1601) */ #define UNIX_EPOCH 11644473600LL /* 1 sec in 100 nsec units */ #define FT_SEC 10000000LL static void ft2tv(FILETIME *src, struct timeval *dst, bool use_epoch) { ULARGE_INTEGER tmp; tmp.LowPart = src->dwLowDateTime; tmp.HighPart = src->dwHighDateTime; dst->tv_sec = (tmp.QuadPart / FT_SEC) - (use_epoch ? UNIX_EPOCH : 0); dst->tv_usec = (tmp.QuadPart % FT_SEC) / 10; } #ifndef HAVE_GETTIMEOFDAY int gettimeofday(struct timeval * tp, void * tzp) { FILETIME file_time; SYSTEMTIME system_time; /* read UTC timestamp */ GetSystemTime(&system_time); SystemTimeToFileTime(&system_time, &file_time); /* convert to timeval */ ft2tv(&file_time, tp, true); return 0; } #endif /* !HAVE_GETTIMEOFDAY */ #ifndef HAVE_LOCALTIME_R struct tm *localtime_r(const time_t *tp, struct tm *dst) { ULARGE_INTEGER utc; FILETIME ft_utc; SYSTEMTIME st_utc, st_local; /* convert time_t to FILETIME */ utc.QuadPart = (*tp + UNIX_EPOCH) * FT_SEC; ft_utc.dwLowDateTime = utc.LowPart; ft_utc.dwHighDateTime = utc.HighPart; /* split to parts and get local time */ if (!FileTimeToSystemTime(&ft_utc, &st_utc)) return NULL; if (!SystemTimeToTzSpecificLocalTime(NULL, &st_utc, &st_local)) return NULL; /* fill struct tm */ dst->tm_sec = st_local.wSecond; dst->tm_min = st_local.wMinute; dst->tm_hour = st_local.wHour; dst->tm_mday = st_local.wDay; dst->tm_mon = st_local.wMonth - 1; dst->tm_year = st_local.wYear - 1900; dst->tm_wday = st_local.wDayOfWeek; dst->tm_yday = 0; dst->tm_isdst = -1; return dst; } #endif /* !HAVE_LOCALTIME_R */ #ifndef HAVE_GETRUSAGE int getrusage(int who, struct rusage *dst) { FILETIME tcreate, texit, tkern, tuser; if (who != RUSAGE_SELF) { errno = EINVAL; return -1; } if (!GetProcessTimes(GetCurrentProcess(), &tcreate, &texit, &tkern, &tuser)) return -1; ft2tv(&tuser, &dst->ru_utime, false); ft2tv(&tkern, &dst->ru_stime, false); return 0; } #endif /* !HAVE_GETRUSAGE */ #endif /* WIN32 */ #ifndef HAVE_TIMEGM time_t timegm(struct tm *tm) { #ifdef WIN32 return _mkgmtime(tm); #else char buf[128], *tz, *old = NULL; time_t secs; tz = getenv("TZ"); if (tz) { old = strdup(tz); if (!old) { strlcpy(buf, tz, sizeof buf); old = buf; } } setenv("TZ", "", 1); tzset(); secs = mktime(tm); if (old) { setenv("TZ", old, 1); } else { unsetenv("TZ"); } tzset(); if (old && old != buf) free(old); return secs; #endif } #endif /* HAVE_TIMEGM */ pgqd/lib/usual/heap.c0000664000401600040160000000740213175113172013037 0ustar cbecbe/* * Binary Heap. * * Copyright (c) 2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include struct Heap { void **data; unsigned allocated; unsigned used; heap_is_better_f is_better; heap_save_pos_f save_pos; CxMem *cx; }; /* * Low-level operations. */ static unsigned get_parent(unsigned i) { return (i - 1) / 2; } static unsigned get_child(unsigned i, unsigned child_nr) { return 2*i + 1 + child_nr; } static bool is_better(struct Heap *h, unsigned i1, unsigned i2) { return h->is_better(h->data[i1], h->data[i2]); } static void set(struct Heap *h, unsigned i, void *ptr) { h->data[i] = ptr; if (h->save_pos) h->save_pos(ptr, i); } static void swap(struct Heap *h, unsigned i1, unsigned i2) { void *tmp = h->data[i1]; set(h, i1, h->data[i2]); set(h, i2, tmp); } static void bubble_up(struct Heap *h, unsigned i) { unsigned p; while (i > 0) { p = get_parent(i); if (!is_better(h, i, p)) break; swap(h, i, p); i = p; } } static void bubble_down(struct Heap *h, unsigned i) { unsigned c = get_child(i, 0); while (c < h->used) { if (c + 1 < h->used) { if (is_better(h, c + 1, c)) c = c + 1; } if (!is_better(h, c, i)) break; swap(h, i, c); i = c; c = get_child(i, 0); } } static void rebalance(struct Heap *h, unsigned pos) { if (pos == 0) { bubble_down(h, pos); } else if (pos == h->used - 1) { bubble_up(h, pos); } else if (is_better(h, pos, get_parent(pos))) { bubble_up(h, pos); } else { bubble_down(h, pos); } } /* * Actual API. */ struct Heap *heap_create(heap_is_better_f is_better_cb, heap_save_pos_f save_pos_cb, CxMem *cx) { struct Heap *h; h = cx_alloc0(cx, sizeof(*h)); if (!h) return NULL; h->save_pos = save_pos_cb; h->is_better = is_better_cb; h->cx = cx; return h; } void heap_destroy(struct Heap *h) { if (h) { cx_free(h->cx, h->data); cx_free(h->cx, h); } } bool heap_reserve(struct Heap *h, unsigned extra) { void *tmp; unsigned newalloc; if (h->used + extra < h->allocated) return true; newalloc = h->allocated * 2; if (newalloc < 32) newalloc = 32; if (newalloc < h->used + extra) newalloc = h->used + extra; tmp = cx_realloc(h->cx, h->data, newalloc * sizeof(void *)); if (!tmp) return false; h->data = tmp; h->allocated = newalloc; return true; } void *heap_top(struct Heap *h) { return (h->used > 0) ? h->data[0] : NULL; } bool heap_push(struct Heap *h, void *ptr) { unsigned pos; if (h->used >= h->allocated) { if (!heap_reserve(h, 1)) return false; } pos = h->used++; set(h, pos, ptr); bubble_up(h, pos); return true; } void *heap_remove(struct Heap *h, unsigned pos) { unsigned last; void *obj; if (pos >= h->used) return NULL; obj = h->data[pos]; last = --h->used; if (pos < last) { set(h, pos, h->data[last]); rebalance(h, pos); } h->data[last] = NULL; return obj; } void *heap_pop(struct Heap *h) { return heap_remove(h, 0); } unsigned heap_size(struct Heap *h) { return h->used; } void *heap_get_obj(struct Heap *h, unsigned pos) { if (pos < h->used) return h->data[pos]; return NULL; } pgqd/lib/usual/crypto/0000775000401600040160000000000013175113172013273 5ustar cbecbepgqd/lib/usual/crypto/sha256.h0000664000401600040160000000372513175113172014463 0ustar cbecbe/* * SHA2-256 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA256 and SHA224 cryptographic hashes. */ #ifndef _USUAL_CRYPTO_SHA256_H_ #define _USUAL_CRYPTO_SHA256_H_ #include /** SHA224 block size in bytes */ #define SHA224_BLOCK_SIZE (16*4) /** SHA256 block size in bytes */ #define SHA256_BLOCK_SIZE (16*4) /** SHA224 result length in bytes */ #define SHA224_DIGEST_LENGTH (224/8) /** SHA256 result length in bytes */ #define SHA256_DIGEST_LENGTH (256/8) /** * State structure for both SHA256 and SHA224. */ struct sha256_ctx { union { uint32_t words[16]; uint8_t raw[16 * 4]; } buf; uint32_t state[8]; uint64_t nbytes; }; /** Initialize structure for SHA256 */ void sha256_reset(struct sha256_ctx *ctx); /** Process more data */ void sha256_update(struct sha256_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha256_final(struct sha256_ctx *ctx, uint8_t *dst); /** Initialize structure for SHA224 */ void sha224_reset(struct sha256_ctx *ctx); /** Process more data */ void sha224_update(struct sha256_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha224_final(struct sha256_ctx *ctx, uint8_t *dst); #endif pgqd/lib/usual/crypto/hmac.c0000664000401600040160000000554013175113172014353 0ustar cbecbe/* * HMAC implementation based on OpenBSD hmac.c * * Copyright (c) 2012 Daniel Farina * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct HMAC { struct DigestContext *hash; CxMem *cx; uint8_t *ipad; uint8_t *opad; }; struct HMAC *hmac_new(const struct DigestInfo *impl, const void *key, unsigned int key_len, CxMem *cx) { struct DigestContext *hash; struct HMAC *hmac; unsigned bs = impl->block_len; unsigned i; /* load hash */ hash = digest_new(impl, cx); if (!hash) return NULL; /* struct setup */ hmac = cx_alloc0(cx, sizeof(struct HMAC) + 2*bs); if (!hmac) { digest_free(hash); return NULL; } hmac->hash = hash; hmac->cx = cx; hmac->ipad = (uint8_t *)(hmac + 1); hmac->opad = hmac->ipad + bs; /* copy key to pads */ if (key_len > bs) { digest_update(hash, key, key_len); digest_final(hash, hmac->ipad); digest_reset(hash); memcpy(hmac->opad, hmac->ipad, digest_result_len(hash)); } else { memcpy(hmac->ipad, key, key_len); memcpy(hmac->opad, key, key_len); } /* calculate pads */ for (i = 0; i < bs; i++) { hmac->ipad[i] ^= 0x36; hmac->opad[i] ^= 0x5c; } /* prepare for user data */ digest_update(hmac->hash, hmac->ipad, bs); return hmac; } /* Free context */ void hmac_free(struct HMAC *ctx) { digest_free(ctx->hash); cx_free(ctx->cx, ctx); } /* Clean HMAC state */ void hmac_reset(struct HMAC *ctx) { unsigned bs = digest_block_len(ctx->hash); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->ipad, bs); } /* Update HMAC state with more data */ void hmac_update(struct HMAC *ctx, const void *data, unsigned int len) { digest_update(ctx->hash, data, len); } /* Get final HMAC result */ void hmac_final(struct HMAC *ctx, uint8_t *dst) { unsigned bs = digest_block_len(ctx->hash); unsigned rs = digest_result_len(ctx->hash); digest_final(ctx->hash, dst); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->opad, bs); digest_update(ctx->hash, dst, rs); digest_final(ctx->hash, dst); } unsigned hmac_block_len(struct HMAC *ctx) { return digest_block_len(ctx->hash); } unsigned hmac_result_len(struct HMAC *ctx) { return digest_result_len(ctx->hash); } pgqd/lib/usual/crypto/md5.h0000664000401600040160000000264013175113172014133 0ustar cbecbe/* * MD5 implementation based on RFC1321. * * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * MD5 cryptographic hash. */ #ifndef _USUAL_CRYPTO_MD5_H_ #define _USUAL_CRYPTO_MD5_H_ #include /** Block length for MD5 */ #define MD5_BLOCK_LENGTH 64 /** Result length for MD5 */ #define MD5_DIGEST_LENGTH 16 /** MD5 state */ struct md5_ctx { uint64_t nbytes; uint32_t a, b, c, d; uint32_t buf[16]; }; /** Clean state */ void md5_reset(struct md5_ctx *ctx); /** Update state with more data */ void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len); /** Get final result */ void md5_final(struct md5_ctx *ctx, uint8_t *dst); #endif pgqd/lib/usual/crypto/csrandom.c0000664000401600040160000000644713175113172015260 0ustar cbecbe/* * Cryptographically Secure Randomness. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #ifdef HAVE_ARC4RANDOM_BUF /* * Simply wrap arc4random_buf() API. */ uint32_t csrandom(void) { return arc4random(); } void csrandom_bytes(void *buf, size_t nbytes) { arc4random_buf(buf, nbytes); } uint32_t csrandom_range(uint32_t upper_bound) { return arc4random_uniform(upper_bound); } #else /* !HAVE_ARC4RANDOM_BUF */ #define USE_KECCAK #ifdef USE_KECCAK /* * Keccak-based PRNG. */ static struct KeccakPRNG prng_keccak; static void impl_init(void) { char buf[32]; if (getentropy(buf, sizeof(buf)) != 0) errx(1, "Cannot get system entropy"); if (!keccak_prng_init(&prng_keccak, 576)) errx(1, "Cannot initialize PRNG"); keccak_prng_add_data(&prng_keccak, buf, sizeof(buf)); explicit_bzero(buf, sizeof(buf)); } static void impl_extract(void *buf, size_t nbytes) { keccak_prng_extract(&prng_keccak, buf, nbytes); } #else /* * ChaCha-based PRNG. */ static struct ChaCha prng_chacha; static void impl_init(void) { uint8_t buf[CHACHA_KEY_SIZE + CHACHA_IV_SIZE]; if (getentropy(buf, sizeof(buf)) != 0) errx(1, "Cannot get system entropy"); chacha_set_key_256(&prng_chacha, buf); chacha_set_nonce(&prng_chacha, 0, 0, buf + CHACHA_KEY_SIZE); explicit_bzero(buf, sizeof(buf)); } static void impl_extract(void *buf, size_t nbytes) { chacha_keystream(&prng_chacha, buf, nbytes); } #endif /* * Locking */ static pid_t last_pid = -1; static void prng_lock(void) { } static void prng_unlock(void) { } /* * Make sure state is initialized. */ static void prng_check_and_lock(void) { bool reseed = false; pid_t new_pid; prng_lock(); new_pid = getpid(); if (new_pid != last_pid) { reseed = true; last_pid = new_pid; } if (reseed) impl_init(); } /* * Public API follows */ void csrandom_bytes(void *buf, size_t nbytes) { prng_check_and_lock(); impl_extract(buf, nbytes); prng_unlock(); } uint32_t csrandom(void) { uint32_t val; csrandom_bytes(&val, sizeof(val)); return val; } uint32_t csrandom_range(uint32_t upper_bound) { uint32_t mod, lim, val; if (upper_bound <= 1) return 0; /* 2**32 % x == (2**32 - x) % x */ mod = -upper_bound % upper_bound; /* wait for value in range [0 .. 2**32-mod) */ lim = -mod; /* loop until good value appears */ while (1) { val = csrandom(); if (val < lim || lim == 0) return val % upper_bound; } } #endif /* !HAVE_ARC4RANDOM_BUF */ pgqd/lib/usual/crypto/chacha.h0000664000401600040160000000327613175113172014663 0ustar cbecbe/* * ChaCha cipher. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * ChaCha cipher. */ #ifndef _CHACHA_CLEAN_H_ #define _CHACHA_CLEAN_H_ #include #define CHACHA_KEY_SIZE 32 #define CHACHA_IV_SIZE 8 #define CHACHA_BLOCK_SIZE 64 /** * ChaCha state. */ struct ChaCha { uint32_t state[16]; union { uint32_t output32[16]; uint8_t output8[16*4]; } u; unsigned int pos; }; /** * Set 256-bit key. */ void chacha_set_key_256(struct ChaCha *ctx, const void *key); /** * Set 128-bit key. */ void chacha_set_key_128(struct ChaCha *ctx, const void *key); /** * Set 2x32-bit counter and 8-byte IV. */ void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv); /** * Extract plain keystream. */ void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes); /** * XOR data with keystream. */ void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes); #endif pgqd/lib/usual/crypto/digest.h0000664000401600040160000000617013175113172014727 0ustar cbecbe/* * Common API for cryptographic digests. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Common API for cryptographic digests. */ #ifndef _USUAL_CRYPTO_DIGEST_H_ #define _USUAL_CRYPTO_DIGEST_H_ #include typedef void (DigestInitFunc)(void *ctx); typedef void (DigestUpdateFunc)(void *ctx, const void *, unsigned); typedef void (DigestFinalFunc)(void *ctx, uint8_t *); /** * Algoright info. */ struct DigestInfo { DigestInitFunc *init; DigestUpdateFunc *update; DigestFinalFunc *final; short state_len; short result_len; short block_len; }; /** * Algoright instance. */ struct DigestContext; /** * Allocate and initialize new algorithm instance. */ struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx); /** Hash more data */ void digest_update(struct DigestContext *ctx, const void *data, size_t len); /** * Get final result. * * To re-use same instance, digest_reset() must be called first. */ void digest_final(struct DigestContext *ctx, uint8_t *res); /** * Prepares instance for new data. */ void digest_reset(struct DigestContext *ctx); /** * Free instance. */ void digest_free(struct DigestContext *ctx); /** * Hash function block length in bytes. */ unsigned digest_block_len(struct DigestContext *ctx); /** * Hash function result length in bytes. */ unsigned digest_result_len(struct DigestContext *ctx); /* * Declare algorithm info's here instead per-also headers * to avoid unnecessary dependencies. */ /** MD5 message digest */ const struct DigestInfo *digest_MD5(void); /** SHA1 message digest */ const struct DigestInfo *digest_SHA1(void); /** SHA224 message digest */ const struct DigestInfo *digest_SHA224(void); /** SHA256 message digest */ const struct DigestInfo *digest_SHA256(void); /** SHA384 message digest */ const struct DigestInfo *digest_SHA384(void); /** SHA512 message digest */ const struct DigestInfo *digest_SHA512(void); /** SHA3-224 message digest */ const struct DigestInfo *digest_SHA3_224(void); /** SHA3-256 message digest */ const struct DigestInfo *digest_SHA3_256(void); /** SHA3-384 message digest */ const struct DigestInfo *digest_SHA3_384(void); /** SHA3-512 message digest */ const struct DigestInfo *digest_SHA3_512(void); /** SHAKE128 in regular digest mode */ const struct DigestInfo *digest_SHAKE128(void); /** SHAKE256 in regular digest mode */ const struct DigestInfo *digest_SHAKE256(void); #endif pgqd/lib/usual/crypto/sha3.c0000664000401600040160000001051713175113172014301 0ustar cbecbe/* * SHA3 implementation. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #define PAD_SHA3 0x06 #define PAD_SHAKE 0x1f void sha3_224_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_224_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_224_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_256_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_256_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_256_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_384_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_384_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_384_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_512_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_512_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_512_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void shake128_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHAKE128_CAPACITY); ctx->padded = 0; ctx->obytes = SHAKE128_DIGEST_LENGTH; ctx->pad = PAD_SHAKE; } void shake256_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHAKE256_CAPACITY); ctx->padded = 0; ctx->obytes = SHAKE256_DIGEST_LENGTH; ctx->pad = PAD_SHAKE; } void sha3_update(struct SHA3Context *ctx, const void *ptr, unsigned len) { keccak_absorb(&ctx->kctx, ptr, len); } void sha3_final(struct SHA3Context *ctx, void *dst) { if (!ctx->padded) { keccak_pad(&ctx->kctx, &ctx->pad, 1); ctx->padded = 1; } keccak_squeeze(&ctx->kctx, dst, ctx->obytes); } void shake_update(struct SHA3Context *ctx, const void *ptr, unsigned len) { keccak_absorb(&ctx->kctx, ptr, len); } void shake_extract(struct SHA3Context *ctx, void *dst, unsigned count) { if (!ctx->padded) { keccak_pad(&ctx->kctx, &ctx->pad, 1); ctx->padded = 1; } keccak_squeeze(&ctx->kctx, dst, count); } /* * DigestInfo */ static const struct DigestInfo sha3_224_info = { (DigestInitFunc *)sha3_224_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_224_DIGEST_LENGTH, SHA3_224_BLOCK_SIZE }; static const struct DigestInfo sha3_256_info = { (DigestInitFunc *)sha3_256_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_256_DIGEST_LENGTH, SHA3_256_BLOCK_SIZE }; static const struct DigestInfo sha3_384_info = { (DigestInitFunc *)sha3_384_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_384_DIGEST_LENGTH, SHA3_384_BLOCK_SIZE }; static const struct DigestInfo sha3_512_info = { (DigestInitFunc *)sha3_512_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_512_DIGEST_LENGTH, SHA3_512_BLOCK_SIZE }; static const struct DigestInfo shake128_info = { (DigestInitFunc *)shake128_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHAKE128_DIGEST_LENGTH, SHAKE128_BLOCK_SIZE }; static const struct DigestInfo shake256_info = { (DigestInitFunc *)shake256_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHAKE256_DIGEST_LENGTH, SHAKE256_BLOCK_SIZE }; const struct DigestInfo *digest_SHA3_224(void) { return &sha3_224_info; } const struct DigestInfo *digest_SHA3_256(void) { return &sha3_256_info; } const struct DigestInfo *digest_SHA3_384(void) { return &sha3_384_info; } const struct DigestInfo *digest_SHA3_512(void) { return &sha3_512_info; } const struct DigestInfo *digest_SHAKE128(void) { return &shake128_info; } const struct DigestInfo *digest_SHAKE256(void) { return &shake256_info; } pgqd/lib/usual/crypto/sha3.h0000664000401600040160000000711013175113172014301 0ustar cbecbe/* * SHA3 implementation. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA3 variants of Keccak. * * SHA3-X are fixed-length hashes, SHAKE is variable-length. */ #ifndef _USUAL_CRYPTO_SHA3_H_ #define _USUAL_CRYPTO_SHA3_H_ #include /** Keccak capacity area for SHA3-224, in bits */ #define SHA3_224_CAPACITY 448 /** Keccak capacity area for SHA3-256, in bits */ #define SHA3_256_CAPACITY 512 /** Keccak capacity area for SHA3-384, in bits */ #define SHA3_384_CAPACITY 768 /** Keccak capacity area for SHA3-512, in bits */ #define SHA3_512_CAPACITY 1024 /** Keccak capacity area for SHAKE128, in bits */ #define SHAKE128_CAPACITY 256 /** Keccak capacity area for SHAKE256, in bits */ #define SHAKE256_CAPACITY 512 /** Result length of SHA3-224, in bytes */ #define SHA3_224_DIGEST_LENGTH (224/8) /** Result length of SHA3-256, in bytes */ #define SHA3_256_DIGEST_LENGTH (256/8) /** Result length of SHA3-384, in bytes */ #define SHA3_384_DIGEST_LENGTH (384/8) /** Result length of SHA3-512, in bytes */ #define SHA3_512_DIGEST_LENGTH (512/8) /** Result length of SHAKE128, in bytes */ #define SHAKE128_DIGEST_LENGTH (256/8) /** Result length of SHAKE256, in bytes */ #define SHAKE256_DIGEST_LENGTH (512/8) /** Block size of SHA3-224, in bytes */ #define SHA3_224_BLOCK_SIZE ((1600 - SHA3_224_CAPACITY) / 8) /** Block size of SHA3-256, in bytes */ #define SHA3_256_BLOCK_SIZE ((1600 - SHA3_256_CAPACITY) / 8) /** Block size of SHA3-384, in bytes */ #define SHA3_384_BLOCK_SIZE ((1600 - SHA3_384_CAPACITY) / 8) /** Block size of SHA3-512, in bytes */ #define SHA3_512_BLOCK_SIZE ((1600 - SHA3_512_CAPACITY) / 8) /** Block size of SHAKE128, in bytes */ #define SHAKE128_BLOCK_SIZE ((1600 - SHAKE128_CAPACITY) / 8) /** Block size of SHAKE256, in bytes */ #define SHAKE256_BLOCK_SIZE ((1600 - SHAKE256_CAPACITY) / 8) /** * State structure. */ struct SHA3Context { struct KeccakContext kctx; bool padded; uint8_t pad; unsigned int obytes; }; /** Initialize state for SHA3-224 */ void sha3_224_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-256 */ void sha3_256_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-384 */ void sha3_384_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-512 */ void sha3_512_reset(struct SHA3Context *ctx); /** Process data, update state */ void sha3_update(struct SHA3Context *ctx, const void *ptr, unsigned len); /** Calculate final result */ void sha3_final(struct SHA3Context *ctx, void *dst); /** Initialize state for SHAKE128 */ void shake128_reset(struct SHA3Context *ctx); /** Initialize state for SHAKE256 */ void shake256_reset(struct SHA3Context *ctx); /** Process data, update state */ void shake_update(struct SHA3Context *ctx, const void *ptr, unsigned len); /** Output variable amount of result data */ void shake_extract(struct SHA3Context *ctx, void *dst, unsigned count); #endif pgqd/lib/usual/crypto/keccak_prng.c0000664000401600040160000000304113175113172015704 0ustar cbecbe/* * PRNG based on Keccak. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include bool keccak_prng_init(struct KeccakPRNG *prng, int capacity) { if (!keccak_init(&prng->ctx, capacity)) return false; prng->extracting = false; prng->have_data = false; return true; } void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len) { if (prng->extracting) { keccak_rewind(&prng->ctx); prng->extracting = false; } keccak_absorb(&prng->ctx, data, len); if (!prng->have_data && len > 0) prng->have_data = true; } bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len) { if (!prng->have_data) return false; if (!prng->extracting) { keccak_pad(&prng->ctx, "\x01", 1); prng->extracting = true; } keccak_squeeze(&prng->ctx, data, len); return true; } pgqd/lib/usual/crypto/sha256.c0000664000401600040160000001327413175113172014456 0ustar cbecbe/* * SHA2-256 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R64(R, t) R16(R, t+0); R16(R, t+16); R16(R, t+32); R16(R, t+48); #define bufpos(ctx) ((ctx)->nbytes & (SHA256_BLOCK_SIZE - 1)) /* * initial values */ static const uint32_t H224[8] = { 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, }; static const uint32_t H256[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, }; /* * constants for mixing */ static const uint32_t K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, }; /* * mixing */ #define CH(x,y,z) ((x & y) ^ ((~x) & z)) #define MAJ(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) #define E0(x) (ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22)) #define E1(x) (ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25)) #define O0(x) (ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3)) #define O1(x) (ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10)) #define W(n) (ctx->buf.words[(n) & 15]) #define setW(n,v) W(n) = (v) #define SHA256_ROUND(_t) do { \ uint32_t tmp1, tmp2, t = (_t); \ if (t >= 16) { \ setW(t, O1(W(t - 2)) + W(t - 7) + O0(W(t - 15)) + W(t - 16)); \ } else { \ /* convert endianess on first go */ \ setW(t, be32toh(W(t))); \ } \ tmp1 = h + E1(e) + CH(e,f,g) + K[k_pos++] + W(t); \ tmp2 = E0(a) + MAJ(a,b,c); \ h = g; g = f; f = e; e = d + tmp1; d = c; c = b; b = a; a = tmp1 + tmp2; \ } while (0) /* * actual core */ static void sha256_core(struct sha256_ctx *ctx) { uint32_t *state = ctx->state; uint32_t a = state[0], b = state[1], c = state[2], d = state[3]; uint32_t e = state[4], f = state[5], g = state[6], h = state[7]; unsigned k_pos = 0; R16(SHA256_ROUND, 0); while (k_pos < 64) { R16(SHA256_ROUND, 16); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; } /* * Public API for SHA256. */ void sha256_reset(struct sha256_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H256, sizeof(H256)); } void sha256_update(struct sha256_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = ctx->buf.raw; while (len > 0) { n = SHA256_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha256_core(ctx); } } void sha256_final(struct sha256_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA256_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); int i; /* add padding */ pad_len = SHA256_BLOCK_SIZE - 8 - pos; if (pad_len <= 0) pad_len += SHA256_BLOCK_SIZE; sha256_update(ctx, padding, pad_len); /* add length */ ctx->buf.words[14] = htobe32(nbits >> 32); ctx->buf.words[15] = htobe32(nbits); /* final result */ sha256_core(ctx); for (i = 0; i < SHA256_DIGEST_LENGTH / 4; i++) be32enc(dst + i*4, ctx->state[i]); } /* * Public API for SHA224. */ void sha224_reset(struct sha256_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H224, sizeof(H224)); } void sha224_update(struct sha256_ctx *ctx, const void *data, unsigned int len) { sha256_update(ctx, data, len); } void sha224_final(struct sha256_ctx *ctx, uint8_t *dst) { uint8_t buf[SHA256_DIGEST_LENGTH]; sha256_final(ctx, buf); memcpy(dst, buf, SHA224_DIGEST_LENGTH); memset(buf, 0, sizeof(buf)); } /* * DigestInfo */ const struct DigestInfo *digest_SHA224(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha224_reset, (DigestUpdateFunc *)sha224_update, (DigestFinalFunc *)sha224_final, sizeof(struct sha256_ctx), SHA224_DIGEST_LENGTH, SHA224_BLOCK_SIZE }; return &info; } const struct DigestInfo *digest_SHA256(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha256_reset, (DigestUpdateFunc *)sha256_update, (DigestFinalFunc *)sha256_final, sizeof(struct sha256_ctx), SHA256_DIGEST_LENGTH, SHA256_BLOCK_SIZE }; return &info; } pgqd/lib/usual/crypto/hmac.h0000664000401600040160000000303013175113172014350 0ustar cbecbe/* * HMAC implementation based on OpenBSD * * Copyright (c) 2012 Daniel Farina * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * HMAC-SHA1 implementation (RFC2104). */ #ifndef _USUAL_CRYPTO_HMAC_H_ #define _USUAL_CRYPTO_HMAC_H_ #include /** HMAC Context */ struct HMAC; /** Create context with key */ struct HMAC *hmac_new(const struct DigestInfo *impl, const void *key, unsigned int key_len, CxMem *cx); /** Free context */ void hmac_free(struct HMAC *ctx); /** Initialize context */ void hmac_reset(struct HMAC *ctx); /** Hash more data */ void hmac_update(struct HMAC *ctx, const void *data, unsigned int len); /** Get final result */ void hmac_final(struct HMAC *ctx, uint8_t *dst); unsigned hmac_block_len(struct HMAC *ctx); unsigned hmac_result_len(struct HMAC *ctx); #endif /* _USUAL_HMAC_H_ */ pgqd/lib/usual/crypto/csrandom.h0000664000401600040160000000231013175113172015246 0ustar cbecbe/* * Cryptographically Secure Randomness. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Cryptographically Secure Randomness. */ #ifndef _USUAL_CRYPTO_CSRANDOM_H_ #define _USUAL_CRYPTO_CSRANDOM_H_ #include /** * Return random uint32_t. */ uint32_t csrandom(void); /** * Return unsigned integer in range. */ uint32_t csrandom_range(uint32_t upper_bound); /** * Fill buffer with random bytes. */ void csrandom_bytes(void *buf, size_t nbytes); #endif pgqd/lib/usual/crypto/keccak_prng.h0000664000401600040160000000330513175113172015714 0ustar cbecbe/* * PRNG based on Keccak. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Implements PRNG mode for Keccak sponge function. */ #ifndef _USUAL_CRYPTO_KECCAK_PRNG_H_ #define _USUAL_CRYPTO_KECCAK_PRNG_H_ #include /** * State structure. */ struct KeccakPRNG { struct KeccakContext ctx; bool extracting; bool have_data; }; /** * Setup Keccak with specified capacity. * * @param prng State structure to be initialized. * @param capacity Keccak capacity in bits. * @return False if invalid capacity, true otherwise. */ bool keccak_prng_init(struct KeccakPRNG *prng, int capacity); /** * Merge entropy data into state. */ void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len); /** * Extract PRNG bytes from state. * * @return True, if extraction was successful. False if state has not been initialzed with keccak_prng_add_data(). */ bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len); #endif pgqd/lib/usual/crypto/keccak.h0000664000401600040160000000450513175113172014671 0ustar cbecbe/* * Keccak implementation. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Simple API to Keccak1600 permutation + sponge. */ #ifndef _USUAL_CRYPTO_KECCAK_H_ #define _USUAL_CRYPTO_KECCAK_H_ #include /** * Keccak state structure for all modes. */ struct KeccakContext { /* 5*5*64 bit state */ union { uint64_t state64[25]; uint32_t state32[2*25]; } u; uint32_t pos; /* current byte position in buffer */ uint32_t rbytes; /* rate (= block size) in bytes */ }; /** * Set up state with specified capacity. * * Returns 1 if successful, 0 if invalid capacity. */ int keccak_init(struct KeccakContext *ctx, unsigned int capacity); /** * Hash additional data. */ void keccak_absorb(struct KeccakContext *ctx, const void *data, size_t len); /** * Extract bytes from state. */ void keccak_squeeze(struct KeccakContext *ctx, uint8_t *dst, size_t len); /** * Extract bytes from state, XOR into data. */ void keccak_squeeze_xor(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * XOR data into state and return it. */ void keccak_encrypt(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * XOR state with data and return it. */ void keccak_decrypt(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * Hash pad suffix. */ void keccak_pad(struct KeccakContext *ctx, const void *data, size_t len); /** * Move internal position to start of buffer. * * Useful for PRNG/duplex modes. */ void keccak_rewind(struct KeccakContext *ctx); /** * Clear rate bits. */ void keccak_forget(struct KeccakContext *ctx); #endif pgqd/lib/usual/crypto/sha1.h0000664000401600040160000000264613175113172014310 0ustar cbecbe/* * SHA1 implementation based on RFC3174. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA1 implementation. */ #ifndef _USUAL_CRYPTO_SHA1_H_ #define _USUAL_CRYPTO_SHA1_H_ #include /** Block length for SHA1 */ #define SHA1_BLOCK_SIZE 64 /** Result length for SHA1 */ #define SHA1_DIGEST_LENGTH 20 /** SHA1 state */ struct sha1_ctx { uint64_t nbytes; uint32_t a, b, c, d, e; uint32_t buf[SHA1_BLOCK_SIZE / 4]; }; /** Clean state */ void sha1_reset(struct sha1_ctx *ctx); /** Update state with more data */ void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len); /** Get final result */ void sha1_final(struct sha1_ctx *ctx, uint8_t *dst); #endif pgqd/lib/usual/crypto/entropy.c0000664000401600040160000001115613175113172015143 0ustar cbecbe/* * Load entropy. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifdef HAVE_LINUX_RANDOM_H #include #include #endif /* * Load system entropy. */ #ifndef HAVE_GETENTROPY /* * win32 */ #if defined(_WIN32) || defined(_WIN64) #define HAVE_getentropy_win32 /* * Windows * * It's possible to get entropy via: * - CryptGenRandom. Uses RtlGenRandom, requires CryptoAPI. * - rand_s(). Uses RtlGenRandom, Requires VS2005 CRT, WindowsXP+. * Missing in mingw32, exists in mingw64. * - RtlGenRandom(). Internal func, no proper public definition. * There is broken def in that does not have NTAPI. * Need to link or load from advapi32.dll. */ typedef BOOLEAN APIENTRY (*rtlgenrandom_t)(void *, ULONG); static int getentropy_win32(void *dst, size_t len) { HMODULE lib; rtlgenrandom_t fn; int res = -1; lib = LoadLibrary("advapi32.dll"); if (lib) { fn = (rtlgenrandom_t)GetProcAddress(lib, "SystemFunction036"); if (fn && fn(dst, len)) res = 0; FreeLibrary(lib); } if (res < 0) errno = EIO; return res; } #endif /* WIN32 */ /* * Linux getrandom() */ #if defined(HAVE_GETRANDOM) || (defined(GRND_RANDOM) && defined(SYS_getrandom)) #define HAVE_getentropy_getrandom #ifndef HAVE_GETRANDOM static int getrandom(void *dst, size_t len, unsigned int flags) { return syscall(SYS_getrandom, dst, len, flags); } #endif static int getentropy_getrandom(void *dst, size_t len) { int res; retry: res = getrandom(dst, len, 0); if (res < 0) { if (errno == EINTR) goto retry; return -1; } if ((size_t)res == len) return 0; errno = EIO; return -1; } #endif /* getrandom */ /* * Generic /dev/urandom */ #ifndef HAVE_getentropy_win32 #define HAVE_getentropy_devrandom #include #include #include /* open and check device node */ static int open_devrandom(const char *dev) { int fd; int oflags = O_RDONLY; #ifdef O_CLOEXEC oflags |= O_CLOEXEC; #endif open_loop: fd = open(dev, oflags); if (fd == -1) { if (errno == EINTR) goto open_loop; return -1; } #ifndef O_CLOEXEC { int res; res = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); if (res != 0) goto fail; } #endif /* * Lightly verify that the device node looks sane */ { struct stat st; if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) goto fail; } #ifdef RNDGETENTCNT { int cnt; if (ioctl(fd, RNDGETENTCNT, &cnt) == -1) goto fail; } #endif /* seems fine */ return fd; fail: close(fd); return -1; } /* * Read normal random devices under /dev. */ static const char *devlist[] = { "/dev/urandom", "/dev/random", NULL, }; static int getentropy_devrandom(void *dst, size_t bytes) { uint8_t *d = dst; size_t need = bytes; int fd, res; unsigned int i; for (i = 0; devlist[i]; i++) { reopen: fd = open_devrandom(devlist[i]); if (fd == -1) continue; while (need > 0) { res = read(fd, d, need); if (res > 0) { /* successful read */ need -= res; d += res; } else if (res == 0) { /* eof - open again */ close(fd); goto reopen; } else if (errno == EINTR) { /* signal - retry read */ } else { close(fd); /* random error, fail */ return -1; } } close(fd); return 0; } errno = EIO; return -1; } #endif /* devrandom */ /* * Export BSD-style getentropy(). */ int getentropy(void *dst, size_t bytes) { int res = -1; int old_errno = errno; if (bytes > 256) { errno = EIO; return res; } #ifdef HAVE_getentropy_win32 if (res != 0) { res = getentropy_win32(dst, bytes); } #endif #ifdef HAVE_getentropy_getrandom if (res != 0) { res = getentropy_getrandom(dst, bytes); } #endif #ifdef HAVE_getentropy_devrandom if (res != 0) { res = getentropy_devrandom(dst, bytes); } #endif if (res == 0) errno = old_errno; return res; } #endif /* !HAVE_GETENTROPY */ pgqd/lib/usual/crypto/sha512.h0000664000401600040160000000372513175113172014456 0ustar cbecbe/* * SHA2-512 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA512 and SHA384 cryptographic hashes. */ #ifndef _USUAL_CRYPTO_SHA512_H_ #define _USUAL_CRYPTO_SHA512_H_ #include /** SHA384 block size in bytes */ #define SHA384_BLOCK_SIZE (16*8) /** SHA512 block size in bytes */ #define SHA512_BLOCK_SIZE (16*8) /** SHA384 result length in bytes */ #define SHA384_DIGEST_LENGTH (384/8) /** SHA512 result length in bytes */ #define SHA512_DIGEST_LENGTH (512/8) /** * State structure for both SHA512 and SHA384. */ struct sha512_ctx { union { uint64_t words[16]; uint8_t raw[16 * 8]; } buf; uint64_t state[8]; uint64_t nbytes; }; /** Initialize structure for SHA512 */ void sha512_reset(struct sha512_ctx *ctx); /** Process more data */ void sha512_update(struct sha512_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha512_final(struct sha512_ctx *ctx, uint8_t *dst); /** Initialize structure for SHA384 */ void sha384_reset(struct sha512_ctx *ctx); /** Process more data */ void sha384_update(struct sha512_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha384_final(struct sha512_ctx *ctx, uint8_t *dst); #endif pgqd/lib/usual/crypto/digest.c0000664000401600040160000000365313175113172014725 0ustar cbecbe/* * Common API for cryptographic digests. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct DigestContext { const struct DigestInfo *impl; CxMem *cx; uint64_t state[1]; }; struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx) { struct DigestContext *ctx; unsigned alloc; alloc = offsetof(struct DigestContext, state) + impl->state_len; ctx = cx_alloc(cx, alloc); if (!ctx) return NULL; ctx->impl = impl; ctx->cx = cx; impl->init(ctx->state); return ctx; } void digest_update(struct DigestContext *ctx, const void *data, size_t len) { ctx->impl->update(ctx->state, data, len); } void digest_final(struct DigestContext *ctx, uint8_t *res) { ctx->impl->final(ctx->state, res); } void digest_reset(struct DigestContext *ctx) { ctx->impl->init(ctx->state); } void digest_free(struct DigestContext *ctx) { CxMem *cx = ctx->cx; unsigned alloc = offsetof(struct DigestContext, state) + ctx->impl->state_len; memset(ctx, 0, alloc); cx_free(cx, ctx); } unsigned digest_block_len(struct DigestContext *ctx) { return ctx->impl->block_len; } unsigned digest_result_len(struct DigestContext *ctx) { return ctx->impl->result_len; } pgqd/lib/usual/crypto/keccak.c0000664000401600040160000010530413175113172014663 0ustar cbecbe/* * Keccak implementation for SHA3 parameters. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on public-domain Keccak-inplace.c and Keccak-inplace32BI.c * implementations from Keccak reference code: * * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, * Michaël Peeters and Gilles Van Assche. For more information, feedback or * questions, please refer to our website: http://keccak.noekeon.org/ * * Implementation by Ronny Van Keer and the designers, * hereby denoted as "the implementer". * * To the extent possible under law, the implementer has waived all copyright * and related or neighboring rights to the source code in this file. * http://creativecommons.org/publicdomain/zero/1.0/ * * 32-bit word interlacing algorithm: * * Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */ #include #include #include #include #include /* For SHA3 variant of Keccak */ #define KECCAK_ROUNDS 24 /* * Enforce minimal code size. If this is not defined, use * faster unrolled implementation. */ /* #define KECCAK_SMALL */ #ifdef KECCAK_SMALL #define KECCAK_64BIT #endif /* * Decide whether to use 64- or 32-bit implementation. */ #if !defined(KECCAK_64BIT) && !defined(KECCAK_32BIT) #if !defined(LONG_MAX) && !defined(UINTPTR_MAX) #error "Need LONG_MAX & UINTPTR_MAX" #endif /* If neither is defined, try to autodetect */ #if (LONG_MAX > 0xFFFFFFFF) || (UINTPTR_MAX > 0xFFFFFFFF) /* use 64-bit implementation if 'long' or 'uintptr_t' is 64-bit */ #define KECCAK_64BIT #else /* otherwise, use 32-bit implementation */ #define KECCAK_32BIT #endif #endif #ifdef KECCAK_64BIT /* * 64-bit implementation - one lane is one 64-bit word. */ /* round constants */ static const uint64_t RoundConstants64[KECCAK_ROUNDS] = { UINT64_C(0x0000000000000001), UINT64_C(0x0000000000008082), UINT64_C(0x800000000000808A), UINT64_C(0x8000000080008000), UINT64_C(0x000000000000808B), UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008009), UINT64_C(0x000000000000008A), UINT64_C(0x0000000000000088), UINT64_C(0x0000000080008009), UINT64_C(0x000000008000000A), UINT64_C(0x000000008000808B), UINT64_C(0x800000000000008B), UINT64_C(0x8000000000008089), UINT64_C(0x8000000000008003), UINT64_C(0x8000000000008002), UINT64_C(0x8000000000000080), UINT64_C(0x000000000000800A), UINT64_C(0x800000008000000A), UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008080), UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008008), }; #ifdef KECCAK_SMALL /* * Minimal code implementation */ static const uint8_t RhoRot[24] = { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 }; static const uint8_t PiLane[24] = { 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 }; static void keccak_f(struct KeccakContext *ctx) { int i, j; uint64_t *A = ctx->u.state64; uint64_t tmpbuf[5 + 2], *tmp = tmpbuf + 1; uint64_t d, c1, c2; for (j = 0; j < KECCAK_ROUNDS; j++) { /* Theta step */ for (i = 0; i < 5; i++) tmp[i] = A[0*5 + i] ^ A[1*5 + i] ^ A[2*5 + i] ^ A[3*5 + i] ^ A[4*5 + i]; tmpbuf[0] = tmp[4]; tmpbuf[6] = tmp[0]; for (i = 0; i < 5; i++) { d = tmp[i-1] ^ rol64(tmp[i+1], 1); A[0 + i] ^= d; A[5 + i] ^= d; A[10 + i] ^= d; A[15 + i] ^= d; A[20 + i] ^= d; } /* Rho + Pi step */ c1 = A[PiLane[23]]; for (i = 0; i < 24; i++) { c2 = A[PiLane[i]]; A[PiLane[i]] = rol64(c1, RhoRot[i]); c1 = c2; } /* Chi step */ for (i = 0; i < 25; ) { tmp[0] = A[i+0]; tmp[1] = A[i+1]; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & tmp[0]; i++; A[i] ^= ~tmp[0] & tmp[1]; i++; } /* Iota step */ A[0] ^= RoundConstants64[j]; } } #else /* !KECCAK_SMALL - fast 64-bit */ static void keccak_f(struct KeccakContext *ctx) { uint64_t *state = ctx->u.state64; uint64_t Ba, Be, Bi, Bo, Bu; uint64_t Ca, Ce, Ci, Co, Cu; uint64_t Da, De, Di, Do, Du; int i; #define Aba state[ 0] #define Abe state[ 1] #define Abi state[ 2] #define Abo state[ 3] #define Abu state[ 4] #define Aga state[ 5] #define Age state[ 6] #define Agi state[ 7] #define Ago state[ 8] #define Agu state[ 9] #define Aka state[10] #define Ake state[11] #define Aki state[12] #define Ako state[13] #define Aku state[14] #define Ama state[15] #define Ame state[16] #define Ami state[17] #define Amo state[18] #define Amu state[19] #define Asa state[20] #define Ase state[21] #define Asi state[22] #define Aso state[23] #define Asu state[24] for (i = 0; i < KECCAK_ROUNDS; i += 4) { /* Code for 4 rounds */ Ca = Aba^Aga^Aka^Ama^Asa; Ce = Abe^Age^Ake^Ame^Ase; Ci = Abi^Agi^Aki^Ami^Asi; Co = Abo^Ago^Ako^Amo^Aso; Cu = Abu^Agu^Aku^Amu^Asu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Age^De), 44); Bi = rol64((Aki^Di), 43); Bo = rol64((Amo^Do), 21); Bu = rol64((Asu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+0]; Age = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Bi = rol64((Aka^Da), 3); Bo = rol64((Ame^De), 45); Bu = rol64((Asi^Di), 61); Ba = rol64((Abo^Do), 28); Be = rol64((Agu^Du), 20); Aka = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Asa^Da), 18); Ba = rol64((Abe^De), 1); Be = rol64((Agi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Amu^Du), 8); Asa = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Be = rol64((Aga^Da), 36); Bi = rol64((Ake^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Aso^Do), 56); Ba = rol64((Abu^Du), 27); Aga = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Bo = rol64((Ama^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Abi^Di), 62); Be = rol64((Ago^Do), 55); Bi = rol64((Aku^Du), 39); Ama = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Ca = Aba^Aka^Asa^Aga^Ama; Ce = Age^Ame^Abe^Ake^Ase; Ci = Aki^Asi^Agi^Ami^Abi; Co = Amo^Abo^Ako^Aso^Ago; Cu = Asu^Agu^Amu^Abu^Aku; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Ame^De), 44); Bi = rol64((Agi^Di), 43); Bo = rol64((Aso^Do), 21); Bu = rol64((Aku^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+1]; Ame = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Bi = rol64((Asa^Da), 3); Bo = rol64((Ake^De), 45); Bu = rol64((Abi^Di), 61); Ba = rol64((Amo^Do), 28); Be = rol64((Agu^Du), 20); Asa = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Ama^Da), 18); Ba = rol64((Age^De), 1); Be = rol64((Asi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Abu^Du), 8); Ama = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Be = rol64((Aka^Da), 36); Bi = rol64((Abe^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Ago^Do), 56); Ba = rol64((Asu^Du), 27); Aka = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Bo = rol64((Aga^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Aki^Di), 62); Be = rol64((Abo^Do), 55); Bi = rol64((Amu^Du), 39); Aga = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Ca = Aba^Asa^Ama^Aka^Aga; Ce = Ame^Ake^Age^Abe^Ase; Ci = Agi^Abi^Asi^Ami^Aki; Co = Aso^Amo^Ako^Ago^Abo; Cu = Aku^Agu^Abu^Asu^Amu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Ake^De), 44); Bi = rol64((Asi^Di), 43); Bo = rol64((Ago^Do), 21); Bu = rol64((Amu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+2]; Ake = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Bi = rol64((Ama^Da), 3); Bo = rol64((Abe^De), 45); Bu = rol64((Aki^Di), 61); Ba = rol64((Aso^Do), 28); Be = rol64((Agu^Du), 20); Ama = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Aga^Da), 18); Ba = rol64((Ame^De), 1); Be = rol64((Abi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Asu^Du), 8); Aga = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Be = rol64((Asa^Da), 36); Bi = rol64((Age^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Abo^Do), 56); Ba = rol64((Aku^Du), 27); Asa = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Bo = rol64((Aka^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Agi^Di), 62); Be = rol64((Amo^Do), 55); Bi = rol64((Abu^Du), 39); Aka = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Ca = Aba^Ama^Aga^Asa^Aka; Ce = Ake^Abe^Ame^Age^Ase; Ci = Asi^Aki^Abi^Ami^Agi; Co = Ago^Aso^Ako^Abo^Amo; Cu = Amu^Agu^Asu^Aku^Abu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Abe^De), 44); Bi = rol64((Abi^Di), 43); Bo = rol64((Abo^Do), 21); Bu = rol64((Abu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+3]; Abe = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Bi = rol64((Aga^Da), 3); Bo = rol64((Age^De), 45); Bu = rol64((Agi^Di), 61); Ba = rol64((Ago^Do), 28); Be = rol64((Agu^Du), 20); Aga = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Aka^Da), 18); Ba = rol64((Ake^De), 1); Be = rol64((Aki^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Aku^Du), 8); Aka = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Be = rol64((Ama^Da), 36); Bi = rol64((Ame^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Amo^Do), 56); Ba = rol64((Amu^Du), 27); Ama = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Bo = rol64((Asa^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Asi^Di), 62); Be = rol64((Aso^Do), 55); Bi = rol64((Asu^Du), 39); Asa = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); } } #endif /* !KECCAK_SMALL */ static inline void xor_lane(struct KeccakContext *ctx, int lane, uint64_t val) { ctx->u.state64[lane] ^= val; } static void extract(uint8_t *dst, const struct KeccakContext *ctx, int startLane, int laneCount) { const uint64_t *src = ctx->u.state64 + startLane; while (laneCount--) { le64enc(dst, *src++); dst += 8; } } #else /* KECCAK_32BIT */ /* * 32-bit implementation - one 64-bit lane is mapped * to two interleaved 32-bit words. */ static const uint32_t RoundConstants32[2*KECCAK_ROUNDS] = { 0x00000001, 0x00000000, 0x00000000, 0x00000089, 0x00000000, 0x8000008b, 0x00000000, 0x80008080, 0x00000001, 0x0000008b, 0x00000001, 0x00008000, 0x00000001, 0x80008088, 0x00000001, 0x80000082, 0x00000000, 0x0000000b, 0x00000000, 0x0000000a, 0x00000001, 0x00008082, 0x00000000, 0x00008003, 0x00000001, 0x0000808b, 0x00000001, 0x8000000b, 0x00000001, 0x8000008a, 0x00000001, 0x80000081, 0x00000000, 0x80000081, 0x00000000, 0x80000008, 0x00000000, 0x00000083, 0x00000000, 0x80008003, 0x00000001, 0x80008088, 0x00000000, 0x80000088, 0x00000001, 0x00008000, 0x00000000, 0x80008082, }; #define KeccakAtoD_round0() \ Cx = Abu0^Agu0^Aku0^Amu0^Asu0; \ Du1 = Abe1^Age1^Ake1^Ame1^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Abu1^Agu1^Aku1^Amu1^Asu1; \ Du0 = Abe0^Age0^Ake0^Ame0^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Abi0^Agi0^Aki0^Ami0^Asi0; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Abi1^Agi1^Aki1^Ami1^Asi1; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Aga0^Aka0^Ama0^Asa0; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Aga1^Aka1^Ama1^Asa1; \ De1 = Cz^Cw; \ \ Cy = Abo1^Ago1^Ako1^Amo1^Aso1; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Abo0^Ago0^Ako0^Amo0^Aso0; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round1() \ Cx = Asu0^Agu0^Amu0^Abu1^Aku1; \ Du1 = Age1^Ame0^Abe0^Ake1^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Asu1^Agu1^Amu1^Abu0^Aku0; \ Du0 = Age0^Ame1^Abe1^Ake0^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Aki1^Asi1^Agi0^Ami1^Abi0; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Aki0^Asi0^Agi1^Ami0^Abi1; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Aka1^Asa0^Aga0^Ama1; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Aka0^Asa1^Aga1^Ama0; \ De1 = Cz^Cw; \ \ Cy = Amo0^Abo1^Ako0^Aso1^Ago0; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Amo1^Abo0^Ako1^Aso0^Ago1; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round2() \ Cx = Aku1^Agu0^Abu1^Asu1^Amu1; \ Du1 = Ame0^Ake0^Age0^Abe0^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Aku0^Agu1^Abu0^Asu0^Amu0; \ Du0 = Ame1^Ake1^Age1^Abe1^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Agi1^Abi1^Asi1^Ami0^Aki1; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Agi0^Abi0^Asi0^Ami1^Aki0; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Asa1^Ama1^Aka1^Aga1; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Asa0^Ama0^Aka0^Aga0; \ De1 = Cz^Cw; \ \ Cy = Aso0^Amo0^Ako1^Ago0^Abo0; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Aso1^Amo1^Ako0^Ago1^Abo1; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round3() \ Cx = Amu1^Agu0^Asu1^Aku0^Abu0; \ Du1 = Ake0^Abe1^Ame1^Age0^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Amu0^Agu1^Asu0^Aku1^Abu1; \ Du0 = Ake1^Abe0^Ame0^Age1^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Asi0^Aki0^Abi1^Ami1^Agi1; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Asi1^Aki1^Abi0^Ami0^Agi0; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Ama0^Aga1^Asa1^Aka0; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Ama1^Aga0^Asa0^Aka1; \ De1 = Cz^Cw; \ \ Cy = Ago1^Aso0^Ako0^Abo0^Amo1; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Ago0^Aso1^Ako1^Abo1^Amo0; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; static void keccak_f(struct KeccakContext *ctx) { uint32_t *state = ctx->u.state32; uint32_t Da0, De0, Di0, Do0, Du0; uint32_t Da1, De1, Di1, Do1, Du1; uint32_t Ca0, Ce0, Ci0, Co0, Cu0; uint32_t Cx, Cy, Cz, Cw; int i; #define Ba Ca0 #define Be Ce0 #define Bi Ci0 #define Bo Co0 #define Bu Cu0 #define Aba0 state[ 0] #define Aba1 state[ 1] #define Abe0 state[ 2] #define Abe1 state[ 3] #define Abi0 state[ 4] #define Abi1 state[ 5] #define Abo0 state[ 6] #define Abo1 state[ 7] #define Abu0 state[ 8] #define Abu1 state[ 9] #define Aga0 state[10] #define Aga1 state[11] #define Age0 state[12] #define Age1 state[13] #define Agi0 state[14] #define Agi1 state[15] #define Ago0 state[16] #define Ago1 state[17] #define Agu0 state[18] #define Agu1 state[19] #define Aka0 state[20] #define Aka1 state[21] #define Ake0 state[22] #define Ake1 state[23] #define Aki0 state[24] #define Aki1 state[25] #define Ako0 state[26] #define Ako1 state[27] #define Aku0 state[28] #define Aku1 state[29] #define Ama0 state[30] #define Ama1 state[31] #define Ame0 state[32] #define Ame1 state[33] #define Ami0 state[34] #define Ami1 state[35] #define Amo0 state[36] #define Amo1 state[37] #define Amu0 state[38] #define Amu1 state[39] #define Asa0 state[40] #define Asa1 state[41] #define Ase0 state[42] #define Ase1 state[43] #define Asi0 state[44] #define Asi1 state[45] #define Aso0 state[46] #define Aso1 state[47] #define Asu0 state[48] #define Asu1 state[49] for (i = 0; i < KECCAK_ROUNDS*2; i += 8) { /* Code for 4 rounds */ KeccakAtoD_round0(); Ba = (Aba0^Da0); Be = rol32((Age0^De0), 22); Bi = rol32((Aki1^Di1), 22); Bo = rol32((Amo1^Do1), 11); Bu = rol32((Asu0^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+0]; Age0 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Age1^De1), 22); Bi = rol32((Aki0^Di0), 21); Bo = rol32((Amo0^Do0), 10); Bu = rol32((Asu1^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+1]; Age1 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Bi = rol32((Aka1^Da1), 2); Bo = rol32((Ame1^De1), 23); Bu = rol32((Asi1^Di1), 31); Ba = rol32((Abo0^Do0), 14); Be = rol32((Agu0^Du0), 10); Aka1 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Aka0^Da0), 1); Bo = rol32((Ame0^De0), 22); Bu = rol32((Asi0^Di0), 30); Ba = rol32((Abo1^Do1), 14); Be = rol32((Agu1^Du1), 10); Aka0 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Asa0^Da0), 9); Ba = rol32((Abe1^De1), 1); Be = rol32((Agi0^Di0), 3); Bi = rol32((Ako1^Do1), 13); Bo = rol32((Amu0^Du0), 4); Asa0 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Bu = rol32((Asa1^Da1), 9); Ba = (Abe0^De0); Be = rol32((Agi1^Di1), 3); Bi = rol32((Ako0^Do0), 12); Bo = rol32((Amu1^Du1), 4); Asa1 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Be = rol32((Aga0^Da0), 18); Bi = rol32((Ake0^De0), 5); Bo = rol32((Ami1^Di1), 8); Bu = rol32((Aso0^Do0), 28); Ba = rol32((Abu1^Du1), 14); Aga0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Be = rol32((Aga1^Da1), 18); Bi = rol32((Ake1^De1), 5); Bo = rol32((Ami0^Di0), 7); Bu = rol32((Aso1^Do1), 28); Ba = rol32((Abu0^Du0), 13); Aga1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Bo = rol32((Ama1^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Abi0^Di0), 31); Be = rol32((Ago1^Do1), 28); Bi = rol32((Aku1^Du1), 20); Ama1 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Bo = rol32((Ama0^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Abi1^Di1), 31); Be = rol32((Ago0^Do0), 27); Bi = rol32((Aku0^Du0), 19); Ama0 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); KeccakAtoD_round1(); Ba = (Aba0^Da0); Be = rol32((Ame1^De0), 22); Bi = rol32((Agi1^Di1), 22); Bo = rol32((Aso1^Do1), 11); Bu = rol32((Aku1^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+2]; Ame1 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Ame0^De1), 22); Bi = rol32((Agi0^Di0), 21); Bo = rol32((Aso0^Do0), 10); Bu = rol32((Aku0^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+3]; Ame0 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Bi = rol32((Asa1^Da1), 2); Bo = rol32((Ake1^De1), 23); Bu = rol32((Abi1^Di1), 31); Ba = rol32((Amo1^Do0), 14); Be = rol32((Agu0^Du0), 10); Asa1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Asa0^Da0), 1); Bo = rol32((Ake0^De0), 22); Bu = rol32((Abi0^Di0), 30); Ba = rol32((Amo0^Do1), 14); Be = rol32((Agu1^Du1), 10); Asa0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Ama1^Da0), 9); Ba = rol32((Age1^De1), 1); Be = rol32((Asi1^Di0), 3); Bi = rol32((Ako0^Do1), 13); Bo = rol32((Abu1^Du0), 4); Ama1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Bu = rol32((Ama0^Da1), 9); Ba = (Age0^De0); Be = rol32((Asi0^Di1), 3); Bi = rol32((Ako1^Do0), 12); Bo = rol32((Abu0^Du1), 4); Ama0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Be = rol32((Aka1^Da0), 18); Bi = rol32((Abe1^De0), 5); Bo = rol32((Ami0^Di1), 8); Bu = rol32((Ago1^Do0), 28); Ba = rol32((Asu1^Du1), 14); Aka1 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Be = rol32((Aka0^Da1), 18); Bi = rol32((Abe0^De1), 5); Bo = rol32((Ami1^Di0), 7); Bu = rol32((Ago0^Do1), 28); Ba = rol32((Asu0^Du0), 13); Aka0 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Bo = rol32((Aga1^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Aki1^Di0), 31); Be = rol32((Abo1^Do1), 28); Bi = rol32((Amu1^Du1), 20); Aga1 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Bo = rol32((Aga0^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Aki0^Di1), 31); Be = rol32((Abo0^Do0), 27); Bi = rol32((Amu0^Du0), 19); Aga0 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); KeccakAtoD_round2(); Ba = (Aba0^Da0); Be = rol32((Ake1^De0), 22); Bi = rol32((Asi0^Di1), 22); Bo = rol32((Ago0^Do1), 11); Bu = rol32((Amu1^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+4]; Ake1 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Ake0^De1), 22); Bi = rol32((Asi1^Di0), 21); Bo = rol32((Ago1^Do0), 10); Bu = rol32((Amu0^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+5]; Ake0 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Bi = rol32((Ama0^Da1), 2); Bo = rol32((Abe0^De1), 23); Bu = rol32((Aki0^Di1), 31); Ba = rol32((Aso1^Do0), 14); Be = rol32((Agu0^Du0), 10); Ama0 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Ama1^Da0), 1); Bo = rol32((Abe1^De0), 22); Bu = rol32((Aki1^Di0), 30); Ba = rol32((Aso0^Do1), 14); Be = rol32((Agu1^Du1), 10); Ama1 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aga1^Da0), 9); Ba = rol32((Ame0^De1), 1); Be = rol32((Abi1^Di0), 3); Bi = rol32((Ako1^Do1), 13); Bo = rol32((Asu1^Du0), 4); Aga1 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aga0^Da1), 9); Ba = (Ame1^De0); Be = rol32((Abi0^Di1), 3); Bi = rol32((Ako0^Do0), 12); Bo = rol32((Asu0^Du1), 4); Aga0 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Be = rol32((Asa1^Da0), 18); Bi = rol32((Age1^De0), 5); Bo = rol32((Ami1^Di1), 8); Bu = rol32((Abo1^Do0), 28); Ba = rol32((Aku0^Du1), 14); Asa1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Be = rol32((Asa0^Da1), 18); Bi = rol32((Age0^De1), 5); Bo = rol32((Ami0^Di0), 7); Bu = rol32((Abo0^Do1), 28); Ba = rol32((Aku1^Du0), 13); Asa0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Bo = rol32((Aka0^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Agi1^Di0), 31); Be = rol32((Amo0^Do1), 28); Bi = rol32((Abu0^Du1), 20); Aka0 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Bo = rol32((Aka1^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Agi0^Di1), 31); Be = rol32((Amo1^Do0), 27); Bi = rol32((Abu1^Du0), 19); Aka1 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); KeccakAtoD_round3(); Ba = (Aba0^Da0); Be = rol32((Abe0^De0), 22); Bi = rol32((Abi0^Di1), 22); Bo = rol32((Abo0^Do1), 11); Bu = rol32((Abu0^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+6]; Abe0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Abe1^De1), 22); Bi = rol32((Abi1^Di0), 21); Bo = rol32((Abo1^Do0), 10); Bu = rol32((Abu1^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+7]; Abe1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Bi = rol32((Aga0^Da1), 2); Bo = rol32((Age0^De1), 23); Bu = rol32((Agi0^Di1), 31); Ba = rol32((Ago0^Do0), 14); Be = rol32((Agu0^Du0), 10); Aga0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Aga1^Da0), 1); Bo = rol32((Age1^De0), 22); Bu = rol32((Agi1^Di0), 30); Ba = rol32((Ago1^Do1), 14); Be = rol32((Agu1^Du1), 10); Aga1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aka0^Da0), 9); Ba = rol32((Ake0^De1), 1); Be = rol32((Aki0^Di0), 3); Bi = rol32((Ako0^Do1), 13); Bo = rol32((Aku0^Du0), 4); Aka0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Bu = rol32((Aka1^Da1), 9); Ba = (Ake1^De0); Be = rol32((Aki1^Di1), 3); Bi = rol32((Ako1^Do0), 12); Bo = rol32((Aku1^Du1), 4); Aka1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Be = rol32((Ama0^Da0), 18); Bi = rol32((Ame0^De0), 5); Bo = rol32((Ami0^Di1), 8); Bu = rol32((Amo0^Do0), 28); Ba = rol32((Amu0^Du1), 14); Ama0 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Be = rol32((Ama1^Da1), 18); Bi = rol32((Ame1^De1), 5); Bo = rol32((Ami1^Di0), 7); Bu = rol32((Amo1^Do1), 28); Ba = rol32((Amu1^Du0), 13); Ama1 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Bo = rol32((Asa0^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Asi0^Di0), 31); Be = rol32((Aso0^Do1), 28); Bi = rol32((Asu0^Du1), 20); Asa0 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Bo = rol32((Asa1^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Asi1^Di1), 31); Be = rol32((Aso1^Do0), 27); Bi = rol32((Asu1^Du0), 19); Asa1 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); } } static void xor_lane(struct KeccakContext *ctx, int lane, uint64_t val) { uint32_t x0, x1, t; uint32_t *dst = ctx->u.state32 + lane*2; x0 = val; t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); x1 = val >> 32; t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); dst[0] ^= (x0 & 0x0000FFFF) | (x1 << 16); dst[1] ^= (x0 >> 16) | (x1 & 0xFFFF0000); } static void extract(uint8_t *dst, const struct KeccakContext *ctx, int startLane, int laneCount) { const uint32_t *src = ctx->u.state32 + startLane * 2; uint32_t t, x0, x1; while (laneCount--) { x0 = *src++; x1 = *src++; t = (x0 & 0x0000FFFF) | (x1 << 16); x1 = (x0 >> 16) | (x1 & 0xFFFF0000); x0 = t; t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); le32enc(dst + 0, x0); le32enc(dst + 4, x1); dst += 8; } } #endif /* KECCAK_32BIT */ /* * Common code */ static void xor_byte(struct KeccakContext *ctx, int nbyte, uint8_t val) { int o = nbyte / 8; int s = (nbyte % 8) * 8; xor_lane(ctx, o, (uint64_t)(val) << s); } static void add_bytes(struct KeccakContext *ctx, const uint8_t *p, unsigned int ofs, unsigned int len) { uint64_t w; unsigned int m = ofs % 8; /* partial word */ if (m) { m = 8 - m; if (m > len) m = len; while (m--) { xor_byte(ctx, ofs++, *p++); len--; } } /* full words */ while (len >= 8) { w = le64dec(p); xor_lane(ctx, ofs / 8, w); ofs += 8; p += 8; len -= 8; } /* partial word */ while (len--) xor_byte(ctx, ofs++, *p++); } static void extract_bytes(struct KeccakContext *ctx, uint8_t *dst, unsigned int ofs, unsigned int count) { uint8_t lanebuf[8]; unsigned int n, avail; if (ofs % 8 != 0 || count < 8) { avail = 8 - ofs % 8; n = (avail > count) ? count : avail; extract(lanebuf, ctx, ofs/8, 1); memcpy(dst, lanebuf + ofs%8, n); dst += n; ofs += n; count -= n; } if (count > 8) { n = count / 8; extract(dst, ctx, ofs/8, n); dst += n*8; ofs += n*8; count -= n*8; } if (count > 0) { extract(lanebuf, ctx, ofs/8, 1); memcpy(dst, lanebuf, count); } memset(lanebuf, 0, sizeof(lanebuf)); } static inline void permute_if_needed(struct KeccakContext *ctx) { if (ctx->pos == ctx->rbytes) { keccak_f(ctx); ctx->pos = 0; } } /* * Public API */ int keccak_init(struct KeccakContext *ctx, unsigned int capacity) { if (capacity % 8 != 0 || capacity < 8 || capacity > (1600 - 8)) return 0; memset(ctx, 0, sizeof(struct KeccakContext)); ctx->rbytes = (1600 - capacity) / 8; return 1; } void keccak_absorb(struct KeccakContext *ctx, const void *data, size_t len) { unsigned int n, avail; const uint8_t *src = data; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; add_bytes(ctx, src, ctx->pos, n); src += n; len -= n; ctx->pos += n; permute_if_needed(ctx); } } void keccak_squeeze(struct KeccakContext *ctx, uint8_t *dst, size_t len) { unsigned int avail, n; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_squeeze_xor(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail, i; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); for (i = 0; i < n; i++) dst[i] ^= src[i]; ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_encrypt(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; add_bytes(ctx, src, ctx->pos, n); extract_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_decrypt(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail, i; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); for (i = 0; i < n; i++) dst[i] ^= src[i]; add_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_pad(struct KeccakContext *ctx, const void *pad, size_t len) { const uint8_t *src = pad; if (len > 0) { if (len > 1) { keccak_absorb(ctx, src, len - 1); src += len - 1; } xor_byte(ctx, ctx->pos, src[0]); xor_byte(ctx, ctx->rbytes - 1, 0x80); } keccak_f(ctx); ctx->pos = 0; } void keccak_rewind(struct KeccakContext *ctx) { ctx->pos = 0; } void keccak_forget(struct KeccakContext *ctx) { unsigned int rem = ctx->rbytes % 8; uint8_t buf[8]; memset(ctx->u.state32, 0, ctx->rbytes - rem); if (rem) { extract_bytes(ctx, buf, ctx->rbytes - rem, rem); add_bytes(ctx, buf, ctx->rbytes - rem, rem); memset(buf, 0, sizeof(buf)); } ctx->pos = 0; } pgqd/lib/usual/crypto/entropy.h0000664000401600040160000000222413175113172015144 0ustar cbecbe/* * Load entropy from kernel. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Load entropy from OS. */ #ifndef _USUAL_CRYPTO_ENTROPY_H_ #define _USUAL_CRYPTO_ENTROPY_H_ #include #ifndef HAVE_GETENTROPY #define getentropy(dst, len) usual_getentropy(dst, len) /** * Fetch entropy from OS kernel. */ int getentropy(void *dst, size_t len); #endif /* !HAVE_GETENTROPY */ #endif /* _USUAL_CRYPTO_ENTROPY_H_ */ pgqd/lib/usual/crypto/chacha.c0000664000401600040160000001051313175113172014646 0ustar cbecbe/* * ChaCha cipher. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on: chacha-ref.c version 20080118 / D. J. Bernstein / Public domain. */ #include #include #include #define CHACHA_ROUNDS 20 #define QUARTERROUND(in, out, a, b, c, d) \ do { \ out[a] = in[a] + in[b]; out[d] = rol32(in[d] ^ out[a], 16); \ out[c] = in[c] + out[d]; out[b] = rol32(in[b] ^ out[c], 12); \ out[a] = out[a] + out[b]; out[d] = rol32(out[d] ^ out[a], 8); \ out[c] = out[c] + out[d]; out[b] = rol32(out[b] ^ out[c], 7); \ } while (0) #define OUTPUT(a,b,c,d) \ do { \ output[a] = htole32(x[a] + input[a]); \ output[b] = htole32(x[b] + input[b]); \ output[c] = htole32(x[c] + input[c]); \ output[d] = htole32(x[d] + input[d]); \ } while (0) /* mix full state. needs 2 call sites to avoid inlining */ static void chacha_mix(struct ChaCha *ctx) { const uint32_t *input = ctx->state; uint32_t *output = ctx->u.output32; int i; uint32_t x[16]; /* first "column" round */ QUARTERROUND(input, x, 0, 4, 8, 12); QUARTERROUND(input, x, 1, 5, 9, 13); QUARTERROUND(input, x, 2, 6, 10, 14); QUARTERROUND(input, x, 3, 7, 11, 15); for (i = 0; i < CHACHA_ROUNDS/2 - 1; i++) { /* "diagonal" round */ QUARTERROUND(x, x, 0, 5, 10, 15); QUARTERROUND(x, x, 1, 6, 11, 12); QUARTERROUND(x, x, 2, 7, 8, 13); QUARTERROUND(x, x, 3, 4, 9, 14); /* "column" round */ QUARTERROUND(x, x, 0, 4, 8, 12); QUARTERROUND(x, x, 1, 5, 9, 13); QUARTERROUND(x, x, 2, 6, 10, 14); QUARTERROUND(x, x, 3, 7, 11, 15); } /* last "diagonal" round */ QUARTERROUND(x, x, 0, 5, 10, 15); OUTPUT(0, 5, 10, 15); QUARTERROUND(x, x, 1, 6, 11, 12); OUTPUT(1, 6, 11, 12); QUARTERROUND(x, x, 2, 7, 8, 13); OUTPUT(2, 7, 8, 13); QUARTERROUND(x, x, 3, 4, 9, 14); OUTPUT(3, 4, 9, 14); ctx->pos = 0; ctx->state[12]++; if (!ctx->state[12]) ctx->state[13]++; } void chacha_set_key_256(struct ChaCha *ctx, const void *key) { unsigned int i; memcpy(&ctx->state[0], "expand 32-byte k", 16); memcpy(&ctx->state[4], key, 32); for (i = 0; i < 12; i++) ctx->state[i] = le32toh(ctx->state[i]); ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_set_key_128(struct ChaCha *ctx, const void *key) { unsigned int i; memcpy(&ctx->state[0], "expand 16-byte k", 16); memcpy(&ctx->state[4], key, 16); memcpy(&ctx->state[8], key, 16); for (i = 0; i < 12; i++) ctx->state[i] = le32toh(ctx->state[i]); ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv) { const uint8_t *_iv = iv; ctx->state[12] = counter_low; ctx->state[13] = counter_high; if (_iv) { ctx->state[14] = le32dec(_iv); ctx->state[15] = le32dec(_iv + 4); } ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes) { unsigned int n, avail; const uint8_t *ks = ctx->u.output8; uint8_t *dst = stream; while (bytes > 0) { if (ctx->pos >= CHACHA_BLOCK_SIZE) chacha_mix(ctx); avail = CHACHA_BLOCK_SIZE - ctx->pos; n = (bytes > avail) ? avail : bytes; memcpy(dst, ks + ctx->pos, n); bytes -= n; dst += n; ctx->pos += n; } } void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes) { unsigned int i, n, avail; const uint8_t *ks = ctx->u.output8; const uint8_t *src = plain; uint8_t *dst = encrypted; while (bytes > 0) { if (ctx->pos >= CHACHA_BLOCK_SIZE) chacha_mix(ctx); avail = CHACHA_BLOCK_SIZE - ctx->pos; n = (bytes > avail) ? avail : bytes; for (i = 0; i < n; i++) dst[i] = src[i] ^ ks[i]; bytes -= n; dst += n; src += n; ctx->pos += n; } } pgqd/lib/usual/crypto/md5.c0000664000401600040160000001304613175113172014130 0ustar cbecbe/* * MD5 implementation based on RFC1321. * * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* * Support functions. */ #define bufpos(ctx) ((ctx)->nbytes & (MD5_BLOCK_LENGTH - 1)) static inline void swap_words(uint32_t *w, int n) { #ifdef WORDS_BIGENDIAN for (; n > 0; w++, n--) *w = le32toh(*w); #endif } /* * MD5 core. */ #define F(X,Y,Z) ((X & Y) | ((~X) & Z)) #define G(X,Y,Z) ((X & Z) | (Y & (~Z))) #define H(X,Y,Z) (X ^ Y ^ Z) #define I(X,Y,Z) (Y ^ (X | (~Z))) #define OP(fn, a, b, c, d, k, s, T_i) \ a = b + rol32(a + fn(b, c, d) + X[k] + T_i, s) static void md5_mix(struct md5_ctx *ctx, const uint32_t *X) { uint32_t a, b, c, d; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; /* Round 1. */ OP(F, a, b, c, d, 0, 7, 0xd76aa478); OP(F, d, a, b, c, 1, 12, 0xe8c7b756); OP(F, c, d, a, b, 2, 17, 0x242070db); OP(F, b, c, d, a, 3, 22, 0xc1bdceee); OP(F, a, b, c, d, 4, 7, 0xf57c0faf); OP(F, d, a, b, c, 5, 12, 0x4787c62a); OP(F, c, d, a, b, 6, 17, 0xa8304613); OP(F, b, c, d, a, 7, 22, 0xfd469501); OP(F, a, b, c, d, 8, 7, 0x698098d8); OP(F, d, a, b, c, 9, 12, 0x8b44f7af); OP(F, c, d, a, b, 10, 17, 0xffff5bb1); OP(F, b, c, d, a, 11, 22, 0x895cd7be); OP(F, a, b, c, d, 12, 7, 0x6b901122); OP(F, d, a, b, c, 13, 12, 0xfd987193); OP(F, c, d, a, b, 14, 17, 0xa679438e); OP(F, b, c, d, a, 15, 22, 0x49b40821); /* Round 2. */ OP(G, a, b, c, d, 1, 5, 0xf61e2562); OP(G, d, a, b, c, 6, 9, 0xc040b340); OP(G, c, d, a, b, 11, 14, 0x265e5a51); OP(G, b, c, d, a, 0, 20, 0xe9b6c7aa); OP(G, a, b, c, d, 5, 5, 0xd62f105d); OP(G, d, a, b, c, 10, 9, 0x02441453); OP(G, c, d, a, b, 15, 14, 0xd8a1e681); OP(G, b, c, d, a, 4, 20, 0xe7d3fbc8); OP(G, a, b, c, d, 9, 5, 0x21e1cde6); OP(G, d, a, b, c, 14, 9, 0xc33707d6); OP(G, c, d, a, b, 3, 14, 0xf4d50d87); OP(G, b, c, d, a, 8, 20, 0x455a14ed); OP(G, a, b, c, d, 13, 5, 0xa9e3e905); OP(G, d, a, b, c, 2, 9, 0xfcefa3f8); OP(G, c, d, a, b, 7, 14, 0x676f02d9); OP(G, b, c, d, a, 12, 20, 0x8d2a4c8a); /* Round 3. */ OP(H, a, b, c, d, 5, 4, 0xfffa3942); OP(H, d, a, b, c, 8, 11, 0x8771f681); OP(H, c, d, a, b, 11, 16, 0x6d9d6122); OP(H, b, c, d, a, 14, 23, 0xfde5380c); OP(H, a, b, c, d, 1, 4, 0xa4beea44); OP(H, d, a, b, c, 4, 11, 0x4bdecfa9); OP(H, c, d, a, b, 7, 16, 0xf6bb4b60); OP(H, b, c, d, a, 10, 23, 0xbebfbc70); OP(H, a, b, c, d, 13, 4, 0x289b7ec6); OP(H, d, a, b, c, 0, 11, 0xeaa127fa); OP(H, c, d, a, b, 3, 16, 0xd4ef3085); OP(H, b, c, d, a, 6, 23, 0x04881d05); OP(H, a, b, c, d, 9, 4, 0xd9d4d039); OP(H, d, a, b, c, 12, 11, 0xe6db99e5); OP(H, c, d, a, b, 15, 16, 0x1fa27cf8); OP(H, b, c, d, a, 2, 23, 0xc4ac5665); /* Round 4. */ OP(I, a, b, c, d, 0, 6, 0xf4292244); OP(I, d, a, b, c, 7, 10, 0x432aff97); OP(I, c, d, a, b, 14, 15, 0xab9423a7); OP(I, b, c, d, a, 5, 21, 0xfc93a039); OP(I, a, b, c, d, 12, 6, 0x655b59c3); OP(I, d, a, b, c, 3, 10, 0x8f0ccc92); OP(I, c, d, a, b, 10, 15, 0xffeff47d); OP(I, b, c, d, a, 1, 21, 0x85845dd1); OP(I, a, b, c, d, 8, 6, 0x6fa87e4f); OP(I, d, a, b, c, 15, 10, 0xfe2ce6e0); OP(I, c, d, a, b, 6, 15, 0xa3014314); OP(I, b, c, d, a, 13, 21, 0x4e0811a1); OP(I, a, b, c, d, 4, 6, 0xf7537e82); OP(I, d, a, b, c, 11, 10, 0xbd3af235); OP(I, c, d, a, b, 2, 15, 0x2ad7d2bb); OP(I, b, c, d, a, 9, 21, 0xeb86d391); ctx->a += a; ctx->b += b; ctx->c += c; ctx->d += d; } /* * Public API. */ void md5_reset(struct md5_ctx *ctx) { ctx->nbytes = 0; ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; } void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *ptr = data; uint8_t *buf = (uint8_t *)ctx->buf; while (len > 0) { n = MD5_BLOCK_LENGTH - bufpos(ctx); if (n > len) n = len; memcpy(buf + bufpos(ctx), ptr, n); ptr += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) { swap_words(ctx->buf, 16); md5_mix(ctx, ctx->buf); } } } void md5_final(struct md5_ctx *ctx, uint8_t *dst) { static const uint8_t padding[MD5_BLOCK_LENGTH] = { 0x80 }; uint64_t final_len = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); /* add padding */ pad_len = MD5_BLOCK_LENGTH - 8 - pos; if (pad_len <= 0) pad_len += MD5_BLOCK_LENGTH; md5_update(ctx, padding, pad_len); /* add length directly */ swap_words(ctx->buf, 14); ctx->buf[14] = final_len; ctx->buf[15] = final_len >> 32; /* final result */ md5_mix(ctx, ctx->buf); le32enc(dst + 0, ctx->a); le32enc(dst + 4, ctx->b); le32enc(dst + 8, ctx->c); le32enc(dst + 12, ctx->d); } /* * DigestInfo */ static const struct DigestInfo md5 = { (DigestInitFunc *)md5_reset, (DigestUpdateFunc *)md5_update, (DigestFinalFunc *)md5_final, sizeof(struct md5_ctx), MD5_DIGEST_LENGTH, MD5_BLOCK_LENGTH }; const struct DigestInfo *digest_MD5(void) { return &md5; } pgqd/lib/usual/crypto/sha1.c0000664000401600040160000000740113175113172014275 0ustar cbecbe/* * SHA1 implementation based on RFC3174. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define bufpos(ctx) ((ctx)->nbytes & (SHA1_BLOCK_SIZE - 1)) /* * SHA1 core. */ #define W(n) (buf[(n) & 15]) #define setW(n, val) W(n) = val /* base SHA1 operation */ #define SHA1OP(_t, fn, K) do { \ uint32_t tmp, t = (_t); \ if (t >= 16) { \ tmp = W(t - 3) ^ W(t - 8) ^ W(t - 14) ^ W(t - 16); \ setW(t, rol32(tmp, 1)); \ } else { \ /* convert endianess on first go */ \ setW(t, be32toh(W(t))); \ } \ tmp = rol32(a, 5) + fn(b, c, d) + e + W(t) + K; \ e = d; d = c; c = rol32(b, 30); b = a; a = tmp; \ } while (0) /* mix functions */ #define F0(b, c, d) (d ^ (b & (c ^ d))) #define F1(b, c, d) (b ^ c ^ d) #define F2(b, c, d) ((b & c) | (b & d) | (c & d)) #define F3(b, c, d) (b ^ c ^ d) /* operation details for each round */ #define SHA1R0(t) SHA1OP(t, F0, 0x5a827999) #define SHA1R1(t) SHA1OP(t, F1, 0x6ed9eba1) #define SHA1R2(t) SHA1OP(t, F2, 0x8f1bbcdc) #define SHA1R3(t) SHA1OP(t, F3, 0xca62c1d6) /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R20(R, t) R16(R, t+0); R4(R, t+16) static void sha1_core(struct sha1_ctx * ctx, uint32_t *buf) { uint32_t a, b, c, d, e; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; e = ctx->e; R20(SHA1R0, 0); R20(SHA1R1, 20); R20(SHA1R2, 40); R20(SHA1R3, 60); ctx->a += a; ctx->b += b; ctx->c += c; ctx->d += d; ctx->e += e; } /* * Public API. */ void sha1_reset(struct sha1_ctx *ctx) { ctx->nbytes = 0; ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->e = 0xc3d2e1f0; } void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = (uint8_t *)ctx->buf; while (len > 0) { n = SHA1_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha1_core(ctx, ctx->buf); } } void sha1_final(struct sha1_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA1_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); /* add padding */ pad_len = SHA1_BLOCK_SIZE - 8 - pos; if (pad_len <= 0) pad_len += SHA1_BLOCK_SIZE; sha1_update(ctx, padding, pad_len); /* add length */ ctx->buf[14] = htobe32(nbits >> 32); ctx->buf[15] = htobe32(nbits); /* final result */ sha1_core(ctx, ctx->buf); be32enc(dst + 0*4, ctx->a); be32enc(dst + 1*4, ctx->b); be32enc(dst + 2*4, ctx->c); be32enc(dst + 3*4, ctx->d); be32enc(dst + 4*4, ctx->e); } /* * DigestInfo */ static const struct DigestInfo sha1_info = { (DigestInitFunc *)sha1_reset, (DigestUpdateFunc *)sha1_update, (DigestFinalFunc *)sha1_final, sizeof(struct sha1_ctx), SHA1_DIGEST_LENGTH, SHA1_BLOCK_SIZE }; const struct DigestInfo *digest_SHA1(void) { return &sha1_info; } pgqd/lib/usual/crypto/sha512.c0000664000401600040160000001705313175113172014450 0ustar cbecbe/* * SHA2-512 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R64(R, t) R16(R, t+0); R16(R, t+16); R16(R, t+32); R16(R, t+48); #define bufpos(ctx) ((ctx)->nbytes & (SHA512_BLOCK_SIZE - 1)) /* * initial values */ static const uint64_t H384[8] = { UINT64_C(0xcbbb9d5dc1059ed8), UINT64_C(0x629a292a367cd507), UINT64_C(0x9159015a3070dd17), UINT64_C(0x152fecd8f70e5939), UINT64_C(0x67332667ffc00b31), UINT64_C(0x8eb44a8768581511), UINT64_C(0xdb0c2e0d64f98fa7), UINT64_C(0x47b5481dbefa4fa4), }; static const uint64_t H512[8] = { UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179), }; /* * constants for mixing */ static const uint64_t K[80] = { UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817), }; /* * mixing */ #define CH(x,y,z) ((x & y) ^ ((~x) & z)) #define MAJ(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) #define E0(x) (ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39)) #define E1(x) (ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41)) #define O0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7)) #define O1(x) (ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6)) #define W(n) (ctx->buf.words[(n) & 15]) #define setW(n,v) W(n) = (v) #define SHA512_ROUND(_t) do { \ uint64_t tmp1, tmp2, t = (_t); \ if (t >= 16) { \ setW(t, O1(W(t - 2)) + W(t - 7) + O0(W(t - 15)) + W(t - 16)); \ } else { \ /* convert endianess on first go */ \ setW(t, be64toh(W(t))); \ } \ tmp1 = h + E1(e) + CH(e,f,g) + K[k_pos++] + W(t); \ tmp2 = E0(a) + MAJ(a,b,c); \ h = g; g = f; f = e; e = d + tmp1; d = c; c = b; b = a; a = tmp1 + tmp2; \ } while (0) /* * actual core */ static void sha512_core(struct sha512_ctx *ctx) { uint64_t *state = ctx->state; uint64_t a = state[0], b = state[1], c = state[2], d = state[3]; uint64_t e = state[4], f = state[5], g = state[6], h = state[7]; unsigned k_pos = 0; R16(SHA512_ROUND, 0); while (k_pos < 80) { R16(SHA512_ROUND, 16); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; } /* * Public API for SHA512. */ void sha512_reset(struct sha512_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H512, sizeof(H512)); } void sha512_update(struct sha512_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = ctx->buf.raw; while (len > 0) { n = SHA512_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha512_core(ctx); } } void sha512_final(struct sha512_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA512_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int i, pad_len; /* add padding */ pad_len = SHA512_BLOCK_SIZE - 16 - bufpos(ctx); if (pad_len <= 0) pad_len += SHA512_BLOCK_SIZE; sha512_update(ctx, padding, pad_len); /* add length */ ctx->buf.words[14] = 0; ctx->buf.words[15] = htobe64(nbits); /* final result */ sha512_core(ctx); for (i = 0; i < SHA512_DIGEST_LENGTH / 8; i++) be64enc(dst + i*8, ctx->state[i]); } /* * Public API for SHA384. */ void sha384_reset(struct sha512_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H384, sizeof(H384)); } void sha384_update(struct sha512_ctx *ctx, const void *data, unsigned int len) { sha512_update(ctx, data, len); } void sha384_final(struct sha512_ctx *ctx, uint8_t *dst) { uint8_t buf[SHA512_DIGEST_LENGTH]; sha512_final(ctx, buf); memcpy(dst, buf, SHA384_DIGEST_LENGTH); memset(buf, 0, sizeof(buf)); } /* * DigestInfo */ const struct DigestInfo *digest_SHA384(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha384_reset, (DigestUpdateFunc *)sha384_update, (DigestFinalFunc *)sha384_final, sizeof(struct sha512_ctx), SHA384_DIGEST_LENGTH, SHA384_BLOCK_SIZE }; return &info; } const struct DigestInfo *digest_SHA512(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha512_reset, (DigestUpdateFunc *)sha512_update, (DigestFinalFunc *)sha512_final, sizeof(struct sha512_ctx), SHA512_DIGEST_LENGTH, SHA512_BLOCK_SIZE }; return &info; } pgqd/lib/usual/socket_win32.h0000664000401600040160000001350313175113172014440 0ustar cbecbe/* * Socket compat code for win32. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_SOCKET_WIN32_H_ #define _USUAL_SOCKET_WIN32_H_ /* if found, likely a mistake */ #undef HAVE_INET_NTOP #undef HAVE_INET_PTON typedef int socklen_t; #define in_addr_t uint32_t /* * make recvmsg/sendmsg and fd related code compile */ struct iovec { void *iov_base; /* Base address. */ size_t iov_len; /* Length. */ }; struct msghdr { void *msg_name; int msg_namelen; struct iovec *msg_iov; int msg_iovlen; void *msg_control; int msg_controllen; int msg_flags; }; #ifndef SCM_RIGHTS #define SCM_RIGHTS 1 #endif #ifndef CMSG_FIRSTHDR struct cmsghdr { int cmsg_len; int cmsg_level; int cmsg_type; }; #define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1)) #define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \ & ~(sizeof (size_t) - 1)) #define CMSG_LEN(len) ((int)(CMSG_ALIGN(sizeof(struct cmsghdr))+(len))) #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= (int)sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->msg_control : \ (struct cmsghdr *)NULL) #define CMSG_NXTHDR(mhdr, cmsg) \ (((cmsg) == NULL) ? CMSG_FIRSTHDR(mhdr) : \ (((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len) \ + CMSG_ALIGN(sizeof(struct cmsghdr)) > \ (u_char *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \ (struct cmsghdr *)NULL : \ (struct cmsghdr *)((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len)))) #define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+CMSG_ALIGN(len)) #endif /* * unify WSAGetLastError() with errno. * * and convert int <-> SOCKET. */ /* int <-> socket */ #define FD2S(fd) ((intptr_t)(fd)) #define S2FD(fd) ((int)(fd)) /* socket <-> HANDLE, plain casts */ #define FD2H(fd) ((HANDLE)FD2S(fd)) #define H2FD(h) S2FD((SOCKET)(h)) static inline int ewrap(int res) { if (res < 0) errno = WSAGetLastError(); return res; } /* proper signature for setsockopt */ static inline int w_setsockopt(int fd, int level, int optname, const void *optval, int optlen) { return ewrap(setsockopt(FD2S(fd), level, optname, optval, optlen)); } #define setsockopt(a,b,c,d,e) w_setsockopt(a,b,c,d,e) /* proper signature for send */ static inline ssize_t w_send(int fd, const void *buf, size_t len, int flags) { return ewrap(send(FD2S(fd), buf, len, flags)); } #define send(a,b,c,d) w_send(a,b,c,d) /* proper signature for recv */ static inline ssize_t w_recv(int fd, void *buf, size_t len, int flags) { return ewrap(recv(FD2S(fd), buf, len, flags)); } #define recv(a,b,c,d) w_recv(a,b,c,d) #define getsockopt(a,b,c,d,e) ewrap(getsockopt(FD2S(a),b,c,d,e)) #define connect(a,b,c) ewrap(connect(FD2S(a),b,c)) #define socket(a,b,c) ewrap(S2FD(socket(a,b,c))) #define bind(a,b,c) ewrap(bind(FD2S(a),b,c)) #define listen(a,b) ewrap(listen(FD2S(a),b)) #define accept(a,b,c) ewrap(accept(FD2S(a),b,c)) #define getpeername(a,b,c) ewrap(getpeername(FD2S(a),b,c)) #define getsockname(a,b,c) ewrap(getsockname(FD2S(a),b,c)) #define select(a,b,c,d,e) ewrap(select(a,b,c,d,e)) static inline struct hostent *w_gethostbyname(const char *n) { struct hostent *res = gethostbyname(n); if (!res) errno = WSAGetLastError(); return res; } #define gethostbyname(a) w_gethostbyname(a) /* make unix socket related code compile */ struct sockaddr_un { short sun_family; char sun_path[128]; }; /* sendmsg is not used */ static inline int sendmsg(int s, const struct msghdr *m, int flags) { if (m->msg_iovlen != 1) { errno = EINVAL; return -1; } return send(s, m->msg_iov[0].iov_base, m->msg_iov[0].iov_len, flags); } /* recvmsg() is, but only with one iov */ static inline int recvmsg(int s, struct msghdr *m, int flags) { if (m->msg_iovlen != 1) { errno = EINVAL; return -1; } if (m->msg_controllen) m->msg_controllen = 0; return recv(s, m->msg_iov[0].iov_base, m->msg_iov[0].iov_len, flags); } /* * fcntl */ #define F_GETFD 1 #define F_SETFD 2 #define F_GETFL 3 #define F_SETFL 4 #define O_NONBLOCK 1 #define FD_CLOEXEC HANDLE_FLAG_INHERIT static inline int fcntl(int fd, int cmd, long arg) { ULONG lval; DWORD dval; switch (cmd) { case F_GETFD: if (GetHandleInformation(FD2H(fd), &dval)) return dval; errno = EINVAL; return -1; case F_SETFD: /* set FD_CLOEXEC */ if (SetHandleInformation(FD2H(fd), FD_CLOEXEC, arg)) return 0; errno = EINVAL; return -1; case F_GETFL: /* O_NONBLOCK? */ return 0; case F_SETFL: /* set O_NONBLOCK */ lval = (arg & O_NONBLOCK) ? 1 : 0; if (ioctlsocket(FD2S(fd), FIONBIO, &lval) == SOCKET_ERROR) { errno = WSAGetLastError(); return -1; } return 0; default: errno = EINVAL; return -1; } } /* * SIO_KEEPALIVE_VALS for mingw32 */ #if !defined(SIO_KEEPALIVE_VALS) #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) struct tcp_keepalive { u_long onoff; u_long keepalivetime; u_long keepaliveinterval; }; #endif /* * Use native poll() if available */ #if !defined(HAVE_POLL) && defined(POLLIN) #define HAVE_POLL #define poll(a,b,c) usual_poll(a,b,c) static inline int poll(struct pollfd *fds, int nfds, int timeout) { return WSAPoll(fds, nfds, timeout); } #endif #endif pgqd/lib/usual/wchar.h0000664000401600040160000000242513175113172013233 0ustar cbecbe/* * wchar.h - wchar_t utilities. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_WCHAR_H_ #define _USUAL_WCHAR_H_ #include #include #include wchar_t *mbstr_decode(const char *str, int str_len, int *wlen_p, wchar_t *wbuf, int wbuf_len, bool allow_invalid); wctype_t wctype_wcsn(const wchar_t *name, unsigned int namelen); #ifndef HAVE_MBSNRTOWCS #define mbsnrtowcs(a,b,c,d,e) usual_mbsnrtowcs(a,b,c,d,e) size_t mbsnrtowcs(wchar_t *dst, const char **src_p, size_t srclen, size_t dstlen, mbstate_t *ps); #endif #endif pgqd/lib/usual/slab.h0000664000401600040160000000447313175113172013055 0ustar cbecbe/* * Primitive slab allocator. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Slab allocator for same-size objects. * * Basic behaviour: * - On each alloc initializer is called. * - if init func is not given, memset() is done * - init func gets either zeroed obj or old obj from _free(). * 'struct List' on obj start is non-zero. * * ATM custom 'align' larger than malloc() alignment does not work. */ #ifndef _USUAL_SLAB_H_ #define _USUAL_SLAB_H_ #include /** Reference to main */ struct Slab; /** Signature for object init function */ typedef void (*slab_init_fn)(void *obj); /** Create new slab context for specific size */ struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx); /** Free whole context */ void slab_destroy(struct Slab *slab); /** Allocate single object from slab cache */ void *slab_alloc(struct Slab *slab) _MALLOC _MUSTCHECK; /** Release single object back */ void slab_free(struct Slab *slab, void *obj); /** Return sum of free and used objects */ int slab_total_count(const struct Slab *slab); /** Return number of free objects in cache */ int slab_free_count(const struct Slab *slab); /** Return number of used objects */ int slab_active_count(const struct Slab *slab); /** Signature for stat info callback */ typedef void (*slab_stat_fn)(void *arg, const char *slab_name, unsigned size, unsigned free, unsigned total); /** Run stat info callback on all slabs */ void slab_stats(slab_stat_fn cb_func, void *cb_arg); #endif pgqd/lib/usual/safeio.c0000664000401600040160000001162013175113172013365 0ustar cbecbe/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Wrappers around regular I/O functions (send/recv/read/write) * that survive EINTR and also can log problems. */ #include #include #include #include #include int safe_read(int fd, void *buf, int len) { int res; loop: res = read(fd, buf, len); if (res < 0 && errno == EINTR) goto loop; return res; } int safe_write(int fd, const void *buf, int len) { int res; loop: res = write(fd, buf, len); if (res < 0 && errno == EINTR) goto loop; return res; } int safe_recv(int fd, void *buf, int len, int flags) { int res; char ebuf[128]; loop: res = recv(fd, buf, len, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_recv(%d, %d) = %s", fd, len, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_recv(%d, %d) = %d", fd, len, res); return res; } int safe_send(int fd, const void *buf, int len, int flags) { int res; char ebuf[128]; loop: res = send(fd, buf, len, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_send(%d, %d) = %s", fd, len, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_send(%d, %d) = %d", fd, len, res); return res; } int safe_close(int fd) { int res; #ifndef WIN32 /* * POSIX says close() can return EINTR but fd state is "undefined" * later. Seems Linux and BSDs close the fd anyway and EINTR is * simply informative. Thus retry is dangerous. */ res = close(fd); #else /* * Seems on windows it can returns proper EINTR but only when * WSACancelBlockingCall() is called. As we don't do it, * ignore EINTR on win32 too. */ res = closesocket(fd); #endif if (res < 0) { char ebuf[128]; log_warning("safe_close(%d) = %s", fd, strerror_r(errno, ebuf, sizeof(ebuf))); } else if (cf_verbose > 2) { log_noise("safe_close(%d) = %d", fd, res); } /* ignore EINTR */ if (res < 0 && errno == EINTR) return 0; return res; } int safe_recvmsg(int fd, struct msghdr *msg, int flags) { int res; char ebuf[128]; loop: res = recvmsg(fd, msg, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_warning("safe_recvmsg(%d, msg, %d) = %s", fd, flags, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_recvmsg(%d, msg, %d) = %d", fd, flags, res); return res; } int safe_sendmsg(int fd, const struct msghdr *msg, int flags) { int res; int msgerr_count = 0; char ebuf[128]; loop: res = sendmsg(fd, msg, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) { log_warning("safe_sendmsg(%d, msg[%d,%d], %d) = %s", fd, (int)msg->msg_iov[0].iov_len, (int)msg->msg_controllen, flags, strerror_r(errno, ebuf, sizeof(ebuf))); /* with ancillary data on blocking socket OSX returns * EMSGSIZE instead of blocking. try to solve it by waiting */ if (errno == EMSGSIZE && msgerr_count < 20) { struct timeval tv = {1, 0}; log_warning("trying to sleep a bit"); select(0, NULL, NULL, NULL, &tv); msgerr_count++; goto loop; } } else if (cf_verbose > 2) log_noise("safe_sendmsg(%d, msg, %d) = %d", fd, flags, res); return res; } int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len) { int res; char buf[128]; char ebuf[128]; loop: res = connect(fd, sa, sa_len); if (res < 0 && errno == EINTR) goto loop; if (res < 0 && (errno != EINPROGRESS || cf_verbose > 2)) log_noise("connect(%d, %s) = %s", fd, sa2str(sa, buf, sizeof(buf)), strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("connect(%d, %s) = %d", fd, sa2str(sa, buf, sizeof(buf)), res); return res; } int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len_p) { int res; char buf[128]; char ebuf[128]; loop: res = accept(fd, sa, sa_len_p); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_accept(%d) = %s", fd, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_accept(%d) = %d (%s)", fd, res, sa2str(sa, buf, sizeof(buf))); return res; } pgqd/lib/usual/string.c0000664000401600040160000003067013175113172013433 0ustar cbecbe/* * String utilities. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #ifdef HAVE_XLOCALE_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif /* * Dynamic list of strings. */ struct StrList { struct StatList list; CxMem *ca; }; struct StrItem { struct List node; const char *str; }; bool strlist_empty(struct StrList *slist) { return statlist_empty(&slist->list); } bool strlist_append(struct StrList *slist, const char *str) { const char *nstr = NULL; bool ok; if (str) { nstr = cx_strdup(slist->ca, str); if (!nstr) return false; } ok = strlist_append_ref(slist, nstr); if (!ok) cx_free(slist->ca, nstr); return ok; } bool strlist_append_ref(struct StrList *slist, const char *str) { struct StrItem *item = cx_alloc(slist->ca, sizeof(*item)); if (!item) return false; list_init(&item->node); item->str = str; statlist_append(&slist->list, &item->node); return true; } const char *strlist_pop(struct StrList *slist) { struct StrItem *item; struct List *el; const char *str; el = statlist_pop(&slist->list); if (!el) return NULL; item = container_of(el, struct StrItem, node); str = item->str; cx_free(slist->ca, item); return str; } struct StrList *strlist_new(CxMem *ca) { struct StrList *slist = cx_alloc0(ca, sizeof(*slist)); if (!slist) return NULL; statlist_init(&slist->list, "strlist"); slist->ca = ca; return slist; } void strlist_free(struct StrList *slist) { const char *s; if (!slist) return; while (!strlist_empty(slist)) { s = strlist_pop(slist); if (s) cx_free(slist->ca, s); } cx_free(slist->ca, slist); } bool strlist_foreach(const struct StrList *slist, str_cb func, void *arg) { struct List *el; struct StrItem *item; statlist_for_each(el, &slist->list) { item = container_of(el, struct StrItem, node); if (!func(arg, item->str)) return false; } return true; } /* * Parse comma separated words. */ static inline const char *skip_ws(const char *p) { while (*p && isspace(*p)) p++; return p; } bool parse_word_list(const char *s, str_cb cb_func, void *cb_arg) { struct MBuf buf; const char *p = s; const char *start, *end; mbuf_init_dynamic(&buf); while (*p) { /* parse word */ p = skip_ws(p); start = p; while (*p && *p != ',') p++; end = p; while (end > start && isspace(*(end - 1))) end--; /* parse comma */ if (*p) { if (*p == ',') { p++; } else { goto failed_syntax; } } /* extract string */ if (!mbuf_write(&buf, start, end - start)) goto failed; if (!mbuf_write_byte(&buf, 0)) goto failed; /* launch callback */ if (!cb_func(cb_arg, (const char *)buf.data)) goto failed; /* reset */ mbuf_rewind_writer(&buf); } mbuf_free(&buf); return true; failed_syntax: errno = EINVAL; failed: mbuf_free(&buf); return false; } /* * Minimal spec-conforming implementations of strlcpy(), strlcat(). */ #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t n) { size_t len = strlen(src); if (len < n) { memcpy(dst, src, len + 1); } else if (n > 0) { memcpy(dst, src, n - 1); dst[n - 1] = 0; } return len; } #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t n) { size_t pos = 0; while (pos < n && dst[pos]) pos++; return pos + strlcpy(dst + pos, src, n - pos); } #endif char *strpcpy(char *dst, const char *src, size_t n) { if (n == 0) return NULL; for (; n > 0; n--, dst++, src++) { if ((*dst = *src) == '\0') return dst; } dst[-1] = '\0'; return NULL; } char *strpcat(char *dst, const char *src, size_t n) { size_t dstlen = strnlen(dst, n); if (dstlen < n) return strpcpy(dst + dstlen, src, n - dstlen); return NULL; } #ifndef HAVE_MEMPCPY void *mempcpy(void *dst, const void *src, size_t n) { memcpy(dst, src, n); return (char *)(dst) + n; } #endif #ifndef HAVE_MEMRCHR void *memrchr(const void *s, int c, size_t n) { const uint8_t *p = s; while (n--) { if (p[n] == c) return (void *)(p + n); } return NULL; } #endif #ifndef HAVE_MEMMEM void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen) { const uint8_t *s = haystack; const uint8_t *q = needle; const uint8_t *s2; size_t i; if (nlen == 0) return (void *)haystack; if (nlen > hlen) return NULL; s2 = memchr(haystack, *q, hlen); if (!s2 || nlen == 1) return (void *)s2; for (i = s2 - s; i <= hlen - nlen; i++) { if (s[i] == q[0] && s[i+1] == q[1]) { if (memcmp(s + i + 2, q + 2, nlen - 2) == 0) return (void *)(s + i); } } return NULL; } #endif #ifndef HAVE_EXPLICIT_BZERO #if defined(_WIN32) && defined(SecureZeroMemory) void explicit_bzero(void *buf, size_t len) { SecureZeroMemory(buf, len); } #elif defined(HAVE_MEMSET_S) void explicit_bzero(void *buf, size_t len) { memset_s(buf, len, 0, len); } #else /* non-win32 */ /* avoid link-time optimization */ #if defined(__GNUC__x) || __has_attribute(weak) void __explicit_bzero_hack(void *, size_t); __attribute__((weak)) void __explicit_bzero_hack(void *buf, size_t len) { } #else typedef void (*__explicit_bzero_cb_t)(void *, size_t); static void __explicit_bzero_hack_cb(void *buf, size_t len) { } static volatile __explicit_bzero_cb_t __explicit_bzero_hack = __explicit_bzero_hack_cb; #endif void explicit_bzero(void *buf, size_t len) { memset(buf, 0, len); __explicit_bzero_hack(buf, len); } #endif #endif /* !_WIN32 */ #ifndef HAVE_BASENAME const char *basename(const char *path) { const char *p, *p2; static char buf[256]; unsigned len; if (path == NULL || path[0] == 0) return memcpy(buf, ".", 2); if ((p = strrchr(path, '/')) == NULL) return path; if (p[1]) return p + 1; /* last char is '/' */ for (p2 = p; p2 > path; p2--) { if (p2[-1] != '/') { len = p2 - path; if (len > sizeof(buf) - 1) len = sizeof(buf) - 1; memcpy(buf, p2 - len, len); buf[len] = 0; return basename(buf); } } /* path contains only '/' chars */ return p; } #endif #ifndef HAVE_DIRNAME const char *dirname(const char *path) { const char *p; size_t len; static char buf[1024]; if (path == NULL || path[0] == 0) return memcpy(buf, ".", 2); /* ignore tailing '/' */ len = strlen(path); while (len && path[len - 1] == '/') len--; if (!len) return memcpy(buf, "/", 2); /* find end of dirname, strip '/' */ if ((p = memrchr(path, '/', len)) == NULL) return memcpy(buf, ".", 2); len = p - path; while (len && path[len - 1] == '/') len--; if (!len) return memcpy(buf, "/", 2); /* return it */ if (len > sizeof(buf) - 1) { errno = ENAMETOOLONG; return NULL; } memcpy(buf, path, len); buf[len] = 0; return buf; } #endif #ifdef WIN32 const char *win32_strerror(int e) { static char buf[1024]; return strerror_r(e, buf, sizeof(buf)); } #endif /* restore original strerror_r() */ #undef strerror_r const char *usual_strerror_r(int e, char *dst, size_t dstlen) { #ifdef WIN32 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), dst, dstlen, NULL); #else /* !WIN32 */ #ifdef STRERROR_R_CHAR_P dst = strerror_r(e, dst, dstlen); #else if (strerror_r(e, dst, dstlen) != 0) strlcpy(dst, "ERR", dstlen); #endif #endif /* !WIN32 */ return dst; } void *mempbrk(const void *data, size_t dlen, const void *find, size_t flen) { const uint8_t *s = data; const uint8_t *fb = find; size_t i; struct Bitmap256 bmap; if (flen == 0) return NULL; if (flen == 1) return memchr(data, fb[0], dlen); bitmap256_init(&bmap); for (i = 0; i < flen; i++) bitmap256_set(&bmap, fb[i]); for (i = 0; i < dlen; i++) { if (bitmap256_is_set(&bmap, s[i])) return (void *)(s + i); } return NULL; } size_t memspn(const void *data, size_t dlen, const void *accept, size_t alen) { const uint8_t *s = data; const uint8_t *fb = accept; size_t i; struct Bitmap256 bmap; if (alen == 0) return 0; if (alen == 1) { for (i = 0; i < dlen; i++) if (s[i] != fb[0]) break; return i; } bitmap256_init(&bmap); for (i = 0; i < alen; i++) bitmap256_set(&bmap, fb[i]); for (i = 0; i < dlen; i++) { if (!bitmap256_is_set(&bmap, s[i])) break; } return i; } size_t memcspn(const void *data, size_t dlen, const void *reject, size_t rlen) { const void *p; p = mempbrk(data, dlen, reject, rlen); if (p != NULL) return (char *)p - (char *)data; return dlen; } double strtod_dot(const char *s, char **tokend) { const char *dp; char buf[128]; char *dst, *tmp, *end, *dot = NULL; unsigned int i, dplen; double res; /* check if locale is sane */ #ifdef HAVE_NL_LANGINFO dp = nl_langinfo(RADIXCHAR); #else dp = localeconv()->decimal_point; #endif if (memcmp(dp, ".", 2) == 0) return strtod(s, tokend); /* try to use proper api */ #ifdef HAVE_STRTOD_L { static locale_t c_locale = NULL; if (!c_locale) c_locale = newlocale(LC_ALL_MASK, "C", NULL); if (c_locale) return strtod_l(s, tokend, c_locale); } #endif while (*s && isspace(*s)) s++; dot = NULL; dst = buf; end = buf + sizeof(buf) - 5; dplen = dp[1] ? strlen(dp) : 1; for (i = 0; s[i]; i++) { if (s[i] >= '0' && s[i] <= '9') { *dst++ = s[i]; } else if (s[i] == '.') { dot = dst; memcpy(dst, dp, dplen); dst += dplen; } else if (s[i] == '-' || s[i] == '+' || s[i] == 'e' || s[i] == 'E') { *dst++ = s[i]; } else { break; } if (dst >= end) { errno = ERANGE; return 0; } } *dst = '\0'; if (!dot) return strtod(s, tokend); tmp = NULL; res = strtod(buf, &tmp); if (tmp && tokend) { *tokend = (char *)s + (tmp - buf); if (dot && tmp > dot && dplen > 1) *tokend -= (dplen - 1); } return res; } ssize_t dtostr_dot(char *buf, size_t buflen, double val) { const char *dp; ssize_t len, dplen; char *p; /* render with max precision */ len = snprintf(buf, buflen, "%.17g", val); if (len >= (ssize_t)buflen || len < 0) return len; /* check if locale is sane */ #ifdef HAVE_NL_LANGINFO dp = nl_langinfo(RADIXCHAR); #else dp = localeconv()->decimal_point; #endif if (memcmp(dp, ".", 2) == 0) return len; dplen = dp[1] ? strlen(dp) : 1; p = memchr(buf, dp[0], len); if (p) { *p = '.'; if (dp[1]) { memmove(p + 1, p + dplen, strlen(p + dplen) + 1); len -= dplen - 1; } } return len; } #ifndef HAVE_STRTONUM long long strtonum(const char *s, long long minval, long long maxval, const char **errstr_p) { char *end = NULL; long long res; int old_errno = errno; if (minval > maxval) goto einval; errno = 0; res = strtoll(s, &end, 10); if (errno == ERANGE) { if (res < 0) goto esmall; else goto elarge; } else if (errno != 0) { goto einval; } else if (*end || end == s) { goto einval; } else if (res < minval) { goto esmall; } else if (res > maxval) { goto elarge; } /* success */ if (errstr_p) *errstr_p = NULL; errno = old_errno; return res; esmall: if (errstr_p) *errstr_p = "too small"; errno = ERANGE; return 0; elarge: if (errstr_p) *errstr_p = "too large"; errno = ERANGE; return 0; einval: if (errstr_p) *errstr_p = "invalid"; errno = EINVAL; return 0; } #endif #ifndef HAVE_STRSEP char *strsep(char **stringp, const char *delim) { char *end, *start = *stringp; if (start) { end = start + strcspn(start, delim); *stringp = *end ? end + 1 : NULL; *end = 0; } return start; } #endif #ifndef HAVE_ASPRINTF int asprintf(char **dst_p, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = vasprintf(dst_p, fmt, ap); va_end(ap); return res; } #endif #ifndef HAVE_VASPRINTF int vasprintf(char **dst_p, const char *fmt, va_list ap) { return cx_vasprintf(NULL, dst_p, fmt, ap); } #endif #ifndef HAVE_STRNLEN size_t strnlen(const char *string, size_t maxlen) { const char *end = memchr(string, '\0', maxlen); return end ? (size_t)(end - string) : maxlen; } #endif pgqd/lib/usual/cxextra.h0000664000401600040160000000326613175113172013611 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Extra allocators for cxalloc. */ #ifndef _USUAL_CXEXTRA_H_ #define _USUAL_CXEXTRA_H_ #include /** Allocator that exits on error. .ctx should be pointer to actual allocator */ extern const struct CxOps cx_nofail_ops; /** nofail for libc */ extern CxMem cx_libc_nofail; /** * Creates allocator that pools all memory together, * without keeping track of single objects, to be * freed all together in one shot. * * realloc(), free() are partially supported for the last * objec only. */ CxMem *cx_new_pool(CxMem *parent, size_t initial_size, unsigned int align); CxMem *cx_new_pool_from_area(CxMem *parent, void *buf, size_t size, bool allow_free, unsigned int align); /** * Creates allocator that remebers all allocations done * under it and allows all of it to be freed together. * * Supports hierarchical trees. */ CxMem *cx_new_tree(CxMem *parent); #endif pgqd/lib/usual/err.c0000664000401600040160000000505713175113172012716 0ustar cbecbe/* * Cmdline error reporting. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #ifndef HAVE_SETPROGNAME static const char *progname; #endif #ifndef HAVE_ERR void err(int e, const char *fmt, ...) { char buf[1024], ebuf[256]; va_list ap; int olderrno = errno; if (fmt) { va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); errx(e, "%s: %s", buf, strerror_r(olderrno, ebuf, sizeof(ebuf))); } else { errx(e, "%s", strerror_r(olderrno, ebuf, sizeof(ebuf))); } } #endif #ifndef HAVE_ERRX void errx(int e, const char *fmt, ...) { va_list ap; if (progname) fprintf(stderr, "%s: ", progname); if (fmt) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } fprintf(stderr, "\n"); exit(e); } #endif #ifndef HAVE_WARN void warn(const char *fmt, ...) { char buf[1024], ebuf[256]; va_list ap; int olderrno = errno; if (fmt) { va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); warnx("%s: %s", buf, strerror_r(olderrno, ebuf, sizeof(ebuf))); } else { warnx("%s", strerror_r(olderrno, ebuf, sizeof(ebuf))); } } #endif #ifndef HAVE_WARNX void warnx(const char *fmt, ...) { va_list ap; if (progname) fprintf(stderr, "%s: ", progname); if (fmt) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } } #endif #ifndef HAVE_SETPROGNAME void setprogname(const char *s) { const char *ss = strrchr(s, '/'); progname = ss ? (ss + 1) : s; } #endif #ifndef HAVE_GETPROGNAME const char *getprogname(void) { return progname; } #endif void *xmalloc(size_t len) { void *p = malloc(len); if (!p) err(1, "no mem"); return p; } void *xrealloc(void *p, size_t len) { void *p2 = realloc(p, len); if (!p2) err(1, "no mem"); return p2; } char *xstrdup(const char *s) { void *s2 = strdup(s); if (!s2) err(1, "no mem"); return s2; } pgqd/lib/usual/cxalloc.c0000664000401600040160000000627313175113172013554 0ustar cbecbe/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* * Utility routines for cx_* API. */ void *cx_alloc(CxMem *cx, size_t len) { if (!len) return NULL; if (!cx) cx = USUAL_ALLOC; return cx->ops->c_alloc(cx->ctx, len); } void *cx_realloc(CxMem *cx, void *ptr, size_t len) { if (!cx) cx = USUAL_ALLOC; if (!ptr) return cx_alloc(cx, len); if (!len) { cx_free(cx, ptr); return NULL; } return cx->ops->c_realloc(cx->ctx, ptr, len); } void cx_free(CxMem *cx, const void *ptr) { if (!cx) cx = USUAL_ALLOC; if (ptr) cx->ops->c_free(cx->ctx, ptr); } void cx_destroy(CxMem *cx) { if (!cx) return; if (!cx->ops->c_destroy) abort(); cx->ops->c_destroy(cx->ctx); } void *cx_alloc0(CxMem *cx, size_t len) { void *p = cx_alloc(cx, len); if (p) memset(p, 0, len); return p; } void *cx_memdup(CxMem *cx, const void *src, size_t len) { void *p = cx_alloc(cx, len); if (p) memcpy(p, src, len); return p; } void *cx_strdup(CxMem *cx, const char *s) { return cx_memdup(cx, s, strlen(s) + 1); } char *cx_sprintf(CxMem *cx, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = cx_vsprintf(cx, fmt, ap); va_end(ap); return res; } char *cx_vsprintf(CxMem *cx, const char *fmt, va_list ap) { char *res; cx_vasprintf(cx, &res, fmt, ap); return res; } int cx_asprintf(CxMem *cx, char **dst_p, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = cx_vasprintf(cx, dst_p, fmt, ap); va_end(ap); return res; } int cx_vasprintf(CxMem *cx, char **dst_p, const char *fmt, va_list ap) { char buf[128], *dst; int res, res2; *dst_p = NULL; res = vsnprintf(buf, sizeof buf, fmt, ap); if (res < 0) return -1; dst = cx_alloc(cx, res + 1); if (!dst) return -1; if ((size_t)res < sizeof buf) { memcpy(dst, buf, res+1); } else { res2 = vsnprintf(dst, res+1, fmt, ap); if (res2 != res) { cx_free(cx, dst); return -1; } } *dst_p = dst; return res; } /* * Base allocator that uses libc routines. */ static void *libc_alloc(void *ctx, size_t len) { return malloc(len); } static void *libc_realloc(void *ctx, void *ptr, size_t len) { return realloc(ptr, len); } static void libc_free(void *ctx, const void *ptr) { free(ptr); } static const struct CxOps libc_alloc_ops = { libc_alloc, libc_realloc, libc_free, }; const struct CxMem cx_libc_allocator = { &libc_alloc_ops, NULL, }; pgqd/lib/usual/talloc.h0000664000401600040160000005516613175113172013417 0ustar cbecbe/* * talloc - implementation of Talloc API. * * Copyright (c) 2013 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Talloc - hierarchical memory allocator. * * This is reimplementation of Samba's talloc: https://talloc.samba.org/ * * Features: * - Any allocated object can be parent to new objects. * - Any object can have more then one parent. * - References. * - Change parent. * - Built on top of API. * * Missing features: * - Pools. Use pools instead. * * It mostly compatible with original so that Samba's documentation is usable, * but it does not try to be bug-for-bug compatible. */ #ifndef _USUAL_TALLOC_H_ #define _USUAL_TALLOC_H_ #include /* avoid cxalloc.h include */ struct CxMem; /** * Type for untyped pointers that are usable as talloc parent. * * For situations where (void*) needs to be used. There * it might be used to use TALLOC_CTX to document that * talloc pointer is needed. */ typedef void TALLOC_CTX; /** * Destructor signature. * * If it returns -1, talloc_free() cancels its operation * and also returns -1. * * @param ptr Object to be freed. * @returns 0 on success, -1 otherwise. */ typedef int (*talloc_destructor_f)(void *ptr); /** * Give name to "unnamed" allocations. * * By default it generates name that contains * talloc API function name, source file and line number. * * It can be redefined in user source files. */ #ifndef TALLOC_POS #define TALLOC_POS(apifunc) apifunc "@" __FILE__ ":" STR(__LINE__) #endif /** * Free and set pointer to NULL. */ #define TALLOC_FREE(ptr) do { talloc_free(ptr); (ptr) = NULL; } while (0) /* * Internal functions used in macros. */ void *_talloc_const_name(const void *parent, size_t elem_size, size_t count, bool zero_fill, const char *name) _MALLOC; void *_talloc_format_name(const void *parent, size_t elem_size, size_t count, bool zero_fill, const char *fmt, ...) _PRINTF(5,6) _MALLOC; int _talloc_unlink(const void *parent, const void *ptr, const char *source_pos); int _talloc_free(const void *ptr, const char *source_pos); void _talloc_free_children(const void *ptr, const char *source_pos); void *_talloc_realloc(const void *parent, void *ptr, size_t elem_size, size_t count, const char *name); void *_talloc_get_type_abort(const void *ptr, const char *name); void *_talloc_move(const void *new_parent, void **ptr_p); void *_talloc_reference_named(const void *new_parent, const void *ptr, const char *name); /** * @name Allocate object and give name based on C type. * * Object names are set automatically based on C type. * * @{ */ /** * Allocate memory for C type. * * Returned object will have memory for sizeof(type) bytes. * * @param parent Parent context or NULL. * @param #type C type of object. * @returns New object or NULL on error. */ #ifdef DOXYGEN type *talloc(const void *parent, #type); #else #define talloc(parent, type) \ (type *)_talloc_const_name(parent, sizeof(type), 1, false, #type) #endif /** * Allocate zero-filled memory for C type. * * Returned object will have memory for sizeof(type) bytes. * * @param parent Parent context or NULL. * @param #type C type of object. * @returns New object or NULL on error. */ #ifdef DOXYGEN type* talloc_zero(const void *parent, #type); #else #define talloc_zero(parent, type) \ (type *)_talloc_const_name(parent, sizeof(type), 1, true, #type) #endif /** * Allocate array of elements of type given. * * size = count * sizeof(type); * * @param parent Parent context or NULL. * @param type C type. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_array(const void *parent, #type, size_t count); #else #define talloc_array(parent, type, count) \ (type *)_talloc_const_name(parent, sizeof(type), count, false, #type) #endif /** * Allocate zero-filled array of elements of type. * * size = count * sizeof(type); * * @param parent Parent context or NULL. * @param type C type. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_zero_array(const void *parent, #type, size_t count); #else #define talloc_zero_array(parent, type, count) \ (type *)_talloc_const_name(parent, sizeof(type), count, true, #type) #endif /** * @} * * @name Allocate object and give custom name. * * @{ */ /** * Allocate named object. * * Name pointer is used directly, so it should not change. * * @param parent Parent context or NULL. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_named_const(const void *parent, size_t size, const char *name); #else #define talloc_named_const(parent, size, name) \ _talloc_const_name(parent, size, 1, false, name) #endif /** * Allocate zero-filled memory for named object. * * Name pointer is used directly, so it should not change. * * @param parent Parent context or NULL. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_zero_named_const(const void *parent, size_t size, const char *name); #else #define talloc_zero_named_const(parent, size, name) \ _talloc_const_name(parent, size, 1, true, name) #endif /** * Allocate named context. * * Name is formatted via talloc_vasprintf() and allocated * as child of main object. * * @param parent Parent context or NULL. * @param size Size for allocation. * @param fmt printf format for name. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_named(const void *parent, size_t size, const char *fmt, ...); #else #define talloc_named(parent, size, ...) \ _talloc_format_name(parent, size, 1, false, __VA_ARGS__) #endif /** * Allocate new top-level named context. * * Name will be allocaten inside context. * * @param fmt Format string for sprintf. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_init(const char *fmt, ...); #else #define talloc_init(...) \ _talloc_format_name(NULL, 0, 0, false, __VA_ARGS__) #endif /** * @} * * @name Allocate unnamed context. * * The objects will get name based on calling location. * * @{ */ /** * Allocate unnamed context. * * @param parent Parent context or NULL. * @param size Size for allocation. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_size(const void *parent, size_t size); #else #define talloc_size(parent, size) \ _talloc_const_name(parent, size, 1, false, TALLOC_POS("talloc_size")) #endif /** * Allocate unnamed context with zeroed memory. * * @param parent Parent context or NULL. * @param size Size for allocation. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_zero_size(const void *parent, size_t size); #else #define talloc_zero_size(parent, size) \ _talloc_const_name(parent, size, 1, true, TALLOC_POS("talloc_zero_size")) #endif /** * Allocate array of elements of type. * * @param parent Parent context or NULL. * @param size Size for one element. * @param count Number of elements. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_array_size(const void *parent, size_t size, size_t count); #else #define talloc_array_size(parent, size, count) \ _talloc_const_name(parent, size, count, false, TALLOC_POS("talloc_array_size")) #endif /** * Allocate unnamed context from typed pointer. * * sizeof(*(ptr)) will be used as allocation size. * * @param parent Parent context or NULL. * @param ptr Typed pointer * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_ptrtype(const void *parent, type *ptr); #else #define talloc_ptrtype(parent, ptr) \ (typeof(ptr))_talloc_const_name(parent, sizeof(*(ptr)), 1, \ false, TALLOC_POS("talloc_ptrtype")) #endif /** * Allocate array of elements of type taken from pointer. * * size = count * sizeof(*ptr); * * @param parent Parent context or NULL. * @param ptr Typed pointer. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_array_ptrtype(const void *parent, type *ptr, size_t count); #else #define talloc_array_ptrtype(parent, ptr, count) \ (typeof(ptr))_talloc_const_name(parent, sizeof(*(ptr)), count, \ false, TALLOC_POS("talloc_array_ptrtype")) #endif /** * Allocate unnamed context as child of parent. * * @param parent Parent context or NULL * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_new(const void *parent); #else #define talloc_new(parent) \ _talloc_const_name(parent, 0, 0, false, TALLOC_POS("talloc_new")) #endif /** * @} * * @name Special contexts. * * Contexts that have unusual behaviour. * * @{ */ /** * Allocate context that will be freed on program exit. * * Objects allocated under this context will be freed * via atexit() handler, unless freed earlier. * This is useful to see leaked memory on program exit. * * @returns New context or NULL on error. */ void *talloc_autofree_context(void); /** * Allocate memory from CxMem. * * Returned pointer and all it's children will be allocated from CxMem; * * Name pointer is used directly, so it should not change. * * @param cx CxMem context to allocate from. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ void *talloc_from_cx(const struct CxMem *cx, size_t size, const char *name); /** * Create CxMem context that uses talloc. * * This makes CxMem-based code work with talloc. * * @param parent Parent context or NULL. * @param name Pointer to static string, will be used directly. * @returns New CxMem context or NULL on error. */ const struct CxMem *talloc_as_cx(const void *parent, const char *name); /** * @} * * @name Free memory. * * @{ */ /** * Set function to be called on final free. */ void talloc_set_destructor(const void *ptr, talloc_destructor_f destructor); #ifndef DOXYGEN #define talloc_set_destructor(ptr, dfn) \ do { int (*_dfn)(typeof(ptr)) = (dfn); \ talloc_set_destructor(ptr, (talloc_destructor_f)(_dfn)); \ } while (0) #endif /** * Free allocated context and all it's children. * * This can be called only on context that have one parent. * Use talloc_unlink() if object may have many references. * * @param ptr Pointer to previously allocated area. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_free(const void *ptr); #else #define talloc_free(ptr) \ _talloc_free(ptr, TALLOC_POS("talloc_free")) #endif /** * @} * * @name Reallocate existing context. * * Only context that have only one reference can be reallocated. * * @{ */ /** * Reallocate array. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param #type C type of one element. * @param count Number of elements. * @returns Reallocated context or NULL on error. */ #ifdef DOXYGEN type *talloc_realloc(const void *parent, const void *ptr, #type, size_t count); #else #define talloc_realloc(parent, ptr, type, count) \ (type *)_talloc_realloc(parent, ptr, sizeof(type), count, #type) #endif /** * Reallocate untyped context. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param size New length in bytes. * @returns Reallocated context or NULL on error. */ #ifdef DOXYGEN void *talloc_realloc_size(const void *parent, void *ptr, size_t size); #else #define talloc_realloc_size(parent, ptr, size) \ _talloc_realloc(parent, ptr, size, 1, TALLOC_POS("talloc_realloc_size")) #endif /** * Function version of realloc. * * This is guaranteed to not be macro, * unlike other API calls. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param size New length in bytes. * @returns Reallocated context or NULL on error. */ void *talloc_realloc_fn(const void *parent, void *ptr, size_t size); /** * @} * * @name Custom object name. * * @{ */ /** * Return object name. * * Result will be always non-NULL. */ const char *talloc_get_name(const void *ptr); /** * Set formatted name. * * Format and allocate as child of ptr. * * @param ptr Pointer to be named. * @param fmt Format string. * @returns New name or NULL on error. */ #ifdef DOXYGEN const char *talloc_set_name(const void *ptr, const char *fmt, ...); #else const char *talloc_set_name(const void *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** * Set name pointer directly. * * @param ptr Pointer to be named. * @param name Pointer to string. */ void talloc_set_name_const(const void *ptr, const char *name); /** * Return same pointer only if name matches, NULL otherwise. */ void *talloc_check_name(const void *ptr, const char *name); /** * @} * * @name Type-based names. * * @{ */ /** * Set context name from type. * * @param ptr Pointer to be named. * @param #type C type. */ #ifdef DOXYGEN void talloc_set_type(const void *ptr, #type); #else #define talloc_set_type(ptr, type) \ talloc_set_name_const(ptr, #type) #endif /** * Get typed pointer only if name matches. * * @param ptr Pointer to be checked. * @param #type C type. */ #ifdef DOXYGEN type *talloc_get_type(const void *ptr, #type); #else #define talloc_get_type(ptr, type) \ (type *)talloc_check_name(ptr, #type) #endif /** * Get typed pointed only if name matches, aborting otherwise. * * This is more extreme version of talloc_get_type(). * * @param ptr Pointer to be checked. * @param #type C type. */ #ifdef DOXYGEN type *talloc_get_type_abort(const void *ptr, #type); #else #define talloc_get_type_abort(ptr, type) \ (type *)_talloc_get_type_abort(ptr, #type) #endif /** * @} * * @name Allocated area size. * * @{ */ /** Get length of allocated area. */ size_t talloc_get_size(const void *ptr); /** * Get number of elements in array. */ #ifdef DOXYGEN size_t talloc_array_length(const type *array); #else #define talloc_array_length(array) (talloc_get_size(array) / sizeof(*(array))) #endif /** * @} * * @name Object parent. * * @{ */ /** Get parent object. */ void *talloc_parent(const void *ptr); /** Get parent object's name. */ const char *talloc_parent_name(const void *ptr); /** Find direct parent based on name */ void *talloc_find_parent_byname(const void *ptr, const char *name); /** Find direct parent based on type */ #ifdef DOXYGEN type *talloc_find_parent_bytype(const void *ptr, #type); #else #define talloc_find_parent_bytype(ptr, type) \ (type *)talloc_find_parent_byname(ptr, #type) #endif /** * @} * * @name Reference handling * * Talloc operates on references, not reference counts. * * @{ */ /** * Create another reference from parent to child. * * @param new_parent New parent context or NULL. * @param ptr Target pointer that is referenced. * @returns Original pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_reference(const void *new_parent, const void *ptr); #else #define talloc_reference(new_parent, ptr) \ (typeof(ptr))_talloc_reference_named(new_parent, ptr, TALLOC_POS("talloc_reference")) #endif /** * Create another reference from NULL context to child. * * @param ptr Target pointer that is referenced. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_increase_ref_count(const void *ptr); #else #define talloc_increase_ref_count(ptr) \ (_talloc_reference_named(NULL, ptr, \ TALLOC_POS("talloc_increase_ref_count")) ? 0 : -1) #endif /** * Remove parent's reference to child. * * If parent is found, unlinks and returns 0, otherwise -1. * * When removing last reference, the object is freed. * * @param parent Parent context or NULL. * @param ptr Pointer to be unlinked. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_unlink(const void *parent, const void *ptr); #else #define talloc_unlink(parent, ptr) \ _talloc_unlink(parent, ptr, TALLOC_POS("talloc_unlink")) #endif /** * Return number of references context has. */ size_t talloc_reference_count(const void *ptr); /** * @} * * @name Change parent * * @{ */ /** * Find reference from old parent switch it to new parent. */ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr); /** * Change primary parent, set old pointer to NULL. * * Useful when moving ponters between structs. * * Cannot be used on pointers that have multiple parents. * * @param new_parent New parent. * @param ptr_p Location of pointer, will be set to NULL if successful. * @returns Original pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_move(const void *new_parent, void **ptr_p); #else #define talloc_move(new_parent, ptr_p) \ (typeof(*(ptr_p)))_talloc_move(new_parent, (void **)(ptr_p)) #endif /** * Change primary parent. * * Cannot be used on pointers that have multiple parents. * * @param new_parent New parent. * @param ptr Pointer to be moved. * @returns Original pointer or NULL on error. */ void *talloc_steal(const void *new_parent, const void *ptr); /** * @} * * @name String functions. * * @{ */ /** Copy memory */ #ifdef DOXYGEN void *talloc_memdup(const void *parent, const void *p, size_t len); #else void *talloc_memdup(const void *parent, const void *p, size_t len) _MALLOC; #endif /** Copy string */ #ifdef DOXYGEN char *talloc_strdup(const void *parent, const char *s); #else char *talloc_strdup(const void *parent, const char *s) _MALLOC; #endif /** Copy string with size limit */ #ifdef DOXYGEN char *talloc_strndup(const void *parent, const char *s, size_t maxlen); #else char *talloc_strndup(const void *parent, const char *s, size_t maxlen) _MALLOC; #endif /** Format string */ #ifdef DOXYGEN char *talloc_asprintf(const void *parent, const char *fmt, ...); #else char *talloc_asprintf(const void *parent, const char *fmt, ...) _PRINTF(2,3) _MALLOC; #endif /** Format string taking argument from va_list */ #ifdef DOXYGEN char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap); #else char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap) _PRINTF(2,0) _MALLOC; #endif /** * @} * * @name String append functions. * * The *_append() functions use strnlen() to get size of string in buffer. * * @{ */ /** Append string to existing string */ char *talloc_strdup_append(char *ptr, const char *s); /** Append string with limit to existing string */ char *talloc_strndup_append(char *ptr, const char *s, size_t maxlen); /** Append formatted string to existing string */ #ifdef DOXYGEN char *talloc_asprintf_append(char *ptr, const char *fmt, ...); #else char *talloc_asprintf_append(char *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** Append formatted string to existing string */ #ifdef DOXYGEN char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap); #else char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap) _PRINTF(2,0); #endif /** * @} * * @name String append to complete buffer. * * The *_append_buffer() functions assume talloc_get_size() will * give the string length with final NUL byte. * * @{ */ /** Append string to existing buffer */ char *talloc_strdup_append_buffer(char *ptr, const char *str); /** Append string with limit to existing buffer */ char *talloc_strndup_append_buffer(char *ptr, const char *str, size_t maxlen); /** Append formatted string to existing buffer */ #ifdef DOXYGEN char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...); #else char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** Append formatted string to existing buffer */ #ifdef DOXYGEN char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap); #else char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap) _PRINTF(2,0); #endif /** * @} * * @name Debugging * * @{ */ /** * Set log function. */ void talloc_set_log_fn(void (*log_fn)(const char *message)); /** * Restore default function. */ void talloc_set_log_stderr(void); /** * Set function to be called on abort. */ void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); /** Collect all parent==NULL allocations under one context */ void talloc_enable_null_tracking(void); /** Collect all parent==NULL allocations under one context, but not autofree */ void talloc_enable_null_tracking_no_autofree(void); /** Stop collecting all parent==NULL allocations under one context */ void talloc_disable_null_tracking(void); /** On program exit, run talloc_report(NULL, stderr) */ void talloc_enable_leak_report(void); /** On program exit, run talloc_report_full(NULL, stderr) */ void talloc_enable_leak_report_full(void); /** * Return allocated bytes under context. */ size_t talloc_total_size(const void *ptr); /** * Return number of contexts under context. */ size_t talloc_total_blocks(const void *ptr); /** * Walk parents */ bool talloc_is_parent(const void *parent, const void *ptr); /** Print report about context and it's immediate childs */ void talloc_report(const void *ptr, FILE *f); /** Print report about context and it's all childs */ void talloc_report_full(const void *ptr, FILE *f); /** Print full report about context, with customizable depth */ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); /** Run callback on context and it's children */ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *private_data), void *private_data); /** Print parents to file */ void talloc_show_parents(const void *ptr, FILE *file); /** * Activate memory limit for object. * * The limit affects only new children allocated after setting it. * It does not take account object itself and it's current children. * * @param ptr Targer pointer. * @param max_size Limit in bytes. * @returns 0 on success, -1 otherwise. */ int talloc_set_memlimit(const void *ptr, size_t max_size); /** @} */ /* deprecated */ #define talloc_free_children(ptr) _talloc_free_children(ptr, TALLOC_POS("talloc_free_children")) /* custom */ void talloc_set_debug(int level); #endif pgqd/lib/usual/fnmatch.c0000664000401600040160000001372213175113172013544 0ustar cbecbe/* * fnmatch.c * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Differences from POSIX: * - ^ can be used in place of ! * - \ is escape in bracket expression, unless FNM_NOESCAPE is given. * - FNM_CASEFOLD * - FNM_LEADING_DIR */ #include #include #include #ifdef NEED_USUAL_FNMATCH /* compare chars with case folding */ static inline bool cmp_fold(wchar_t c1, wchar_t c2, int flags) { if (c1 == c2) return true; if (flags & FNM_CASEFOLD) { if (iswupper(c1) && iswlower(c2)) return c1 == (wchar_t)towupper(c2); else if (iswlower(c1) && iswupper(c2)) return c1 == (wchar_t)towlower(c2); } return false; } /* compare char to range with case folding */ static inline bool range_fold(wchar_t c, wchar_t r1, wchar_t r2, int flags) { if (c >= r1 && c <= r2) return true; if (flags & FNM_CASEFOLD) { /* convert only if it makes sense */ if (iswupper(c) && iswlower(r1) && iswlower(r2)) { c = towlower(c); if (c >= r1 && c <= r2) return true; } else if (iswlower(c) && iswupper(r1) && iswupper(r2)) { c = towupper(c); if (c >= r1 && c <= r2) return true; } } return false; } /* match bracket expression */ static const wchar_t *match_class(const wchar_t *pat, wchar_t c, int flags) { const wchar_t *p = pat; const wchar_t *start; bool neg = false; bool match = false; bool fallback_ok = true; const wchar_t *n1, *n2; wctype_t wct; /* negation */ if (*p == '!' || *p == '^') { neg = true; p++; } start = p; loop: /* named class, equivalence class or collating symbol */ if (p[0] == '[' && (p[1] == ':' || p[1] == '.' || p[1] == '=')) { n1 = p + 2; n2 = wcschr(n1, p[1]); if (!n2 || n2[1] != ']') goto parse_fail; if (p[1] != ':') return NULL; p = n2 + 2; wct = wctype_wcsn(n1, n2-n1); if (wct == (wctype_t)0) return NULL; if (iswctype(c, wct)) match = true; fallback_ok = false; /* skip rest */ goto loop; } parse_fail: /* unexpected pattern end */ if (p[0] == '\0') { /* only open bracket exists, take it as literal */ if (fallback_ok && c == '[') return pat - 1; return NULL; } /* closing bracket */ if (p[0] == ']' && p != start) return (match ^ neg) ? p : NULL; /* escape next char */ if (p[0] == '\\' && !(flags & FNM_NOESCAPE)) { if (p[1] == '\0') return NULL; p++; } /* its either simple range or char */ if (p[1] == '-' && p[2] != ']' && p[2] != '\0') { wchar_t r1 = p[0]; wchar_t r2 = p[2]; if (r2 == '\\' && !(flags & FNM_NOESCAPE)) { p++; r2 = p[2]; if (r2 == '\0') return NULL; } if (range_fold(c, r1, r2, flags)) match = true; p += 3; } else { if (cmp_fold(c, p[0], flags)) match = true; p++; } goto loop; } /* * FNM_PATHNAME disallows wildcard match for '/', * FNM_PERIOD disallows wildcard match for leading '.', * check for string end also. */ static bool disallow_wildcard(const wchar_t *s, const wchar_t *str, int flags) { if (*s == '\0') return true; if (*s == '/') return (flags & FNM_PATHNAME); if (*s == '.' && (flags & FNM_PERIOD)) { if (s == str) return true; if (s[-1] == '/' && (flags & FNM_PATHNAME)) return true; } return false; } /* * Non-recursive fnmatch(), based on globmatch() by */ static int wfnmatch(const wchar_t *pat, const wchar_t *str, int flags) { const wchar_t *p = pat; const wchar_t *s = str; const wchar_t *retry_p = NULL; const wchar_t *skip_s = NULL; loop: switch (*p) { case '*': /* match any number of chars from this position on */ retry_p = p + 1; skip_s = s; /* dot after '*' must not match leading dot */ if (p[1] == '.' && disallow_wildcard(s, str, flags)) return FNM_NOMATCH; break; case '?': /* match any char */ if (disallow_wildcard(s, str, flags)) goto nomatch_retry; s++; break; case '[': /* match character class */ if (disallow_wildcard(s, str, flags)) goto nomatch_retry; p = match_class(p + 1, *s, flags); if (p == NULL) goto nomatch_retry; s++; break; case '\\': /* escape next char */ if (!(flags & FNM_NOESCAPE)) { p++; if (*p == '\0') return FNM_NOMATCH; } default: /* match single char */ if (*s == '/' && *p == '\0' && (flags & FNM_LEADING_DIR)) return 0; if (!cmp_fold(*p, *s, flags)) goto nomatch_retry; if (*s == '\0') return 0; s++; } p++; goto loop; nomatch_retry: /* eat chars with '*', if possible */ if (retry_p == NULL || *s == '\0') return FNM_NOMATCH; s = skip_s++; p = retry_p; if (*s == '\0') return (*p == '\0') ? 0 : FNM_NOMATCH; if (disallow_wildcard(s, str, flags)) return FNM_NOMATCH; s++; goto loop; } /* * Convert locale-specific encoding to wchar_t string */ int fnmatch(const char *pat, const char *str, int flags) { const wchar_t *wpat, *wstr; wchar_t pbuf[128]; wchar_t sbuf[128]; int plen = strlen(pat); int slen = strlen(str); int res; /* convert encoding */ wpat = mbstr_decode(pat, plen, NULL, pbuf, sizeof(pbuf) / sizeof(wchar_t), false); if (!wpat) return (errno == EILSEQ) ? FNM_NOMATCH : -1; wstr = mbstr_decode(str, slen, NULL, sbuf, sizeof(sbuf) / sizeof(wchar_t), true); if (!wstr) return -1; /* run actual fnmatch */ res = wfnmatch(wpat, wstr, flags); /* free buffers */ if (wstr != sbuf) free(wstr); if (wpat != pbuf) free(wpat); return res; } #endif pgqd/lib/usual/pgutil_kwlookup.h0000664000401600040160000005374313175113172015377 0ustar cbecbe/* ANSI-C code produced by gperf version 3.0.3 */ /* Command-line: gperf -m5 usual/pgutil_kwlookup.g */ /* Computed positions: -k'1-2,6,9,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif /* maximum key range = 296, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int pg_keyword_lookup_hash (register const char *str, register unsigned int len) { static const unsigned short asso_values[] = { 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 38, 125, 31, 64, 10, 96, 60, 125, 26, 7, 5, 13, 63, 10, 12, 70, 312, 5, 19, 3, 71, 131, 65, 50, 77, 3, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312 }; register int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[8]]; /*FALLTHROUGH*/ case 8: case 7: case 6: hval += asso_values[(unsigned char)str[5]]; /*FALLTHROUGH*/ case 5: case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } #ifdef __GNUC__ __inline #ifdef __GNUC_STDC_INLINE__ __attribute__ ((__gnu_inline__)) #endif #endif const char * pg_keyword_lookup_real (register const char *str, register unsigned int len) { enum { TOTAL_KEYWORDS = 148, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 17, MIN_HASH_VALUE = 16, MAX_HASH_VALUE = 311 }; struct pgkw_t { char pgkw_str16[sizeof("treat")]; char pgkw_str22[sizeof("true")]; char pgkw_str24[sizeof("or")]; char pgkw_str27[sizeof("order")]; char pgkw_str28[sizeof("not")]; char pgkw_str29[sizeof("to")]; char pgkw_str30[sizeof("left")]; char pgkw_str31[sizeof("least")]; char pgkw_str32[sizeof("real")]; char pgkw_str33[sizeof("join")]; char pgkw_str34[sizeof("on")]; char pgkw_str36[sizeof("none")]; char pgkw_str37[sizeof("else")]; char pgkw_str39[sizeof("right")]; char pgkw_str41[sizeof("select")]; char pgkw_str42[sizeof("int")]; char pgkw_str43[sizeof("time")]; char pgkw_str44[sizeof("inout")]; char pgkw_str45[sizeof("some")]; char pgkw_str46[sizeof("inner")]; char pgkw_str47[sizeof("limit")]; char pgkw_str48[sizeof("in")]; char pgkw_str51[sizeof("nchar")]; char pgkw_str52[sizeof("into")]; char pgkw_str53[sizeof("like")]; char pgkw_str54[sizeof("ilike")]; char pgkw_str55[sizeof("notnull")]; char pgkw_str56[sizeof("table")]; char pgkw_str57[sizeof("localtime")]; char pgkw_str58[sizeof("integer")]; char pgkw_str60[sizeof("cross")]; char pgkw_str62[sizeof("create")]; char pgkw_str63[sizeof("collate")]; char pgkw_str64[sizeof("references")]; char pgkw_str66[sizeof("is")]; char pgkw_str67[sizeof("all")]; char pgkw_str68[sizeof("analyze")]; char pgkw_str69[sizeof("column")]; char pgkw_str70[sizeof("intersect")]; char pgkw_str71[sizeof("constraint")]; char pgkw_str72[sizeof("except")]; char pgkw_str73[sizeof("grant")]; char pgkw_str75[sizeof("trim")]; char pgkw_str76[sizeof("cast")]; char pgkw_str77[sizeof("isnull")]; char pgkw_str78[sizeof("as")]; char pgkw_str79[sizeof("national")]; char pgkw_str80[sizeof("coalesce")]; char pgkw_str83[sizeof("case")]; char pgkw_str84[sizeof("analyse")]; char pgkw_str85[sizeof("row")]; char pgkw_str86[sizeof("greatest")]; char pgkw_str87[sizeof("end")]; char pgkw_str88[sizeof("new")]; char pgkw_str89[sizeof("out")]; char pgkw_str90[sizeof("do")]; char pgkw_str91[sizeof("asc")]; char pgkw_str92[sizeof("old")]; char pgkw_str93[sizeof("outer")]; char pgkw_str95[sizeof("similar")]; char pgkw_str96[sizeof("union")]; char pgkw_str97[sizeof("default")]; char pgkw_str98[sizeof("null")]; char pgkw_str99[sizeof("user")]; char pgkw_str100[sizeof("leading")]; char pgkw_str101[sizeof("extract")]; char pgkw_str102[sizeof("trailing")]; char pgkw_str103[sizeof("only")]; char pgkw_str104[sizeof("exists")]; char pgkw_str106[sizeof("natural")]; char pgkw_str107[sizeof("unique")]; char pgkw_str108[sizeof("dec")]; char pgkw_str109[sizeof("desc")]; char pgkw_str111[sizeof("distinct")]; char pgkw_str112[sizeof("deferrable")]; char pgkw_str115[sizeof("and")]; char pgkw_str116[sizeof("for")]; char pgkw_str117[sizeof("float")]; char pgkw_str119[sizeof("smallint")]; char pgkw_str120[sizeof("offset")]; char pgkw_str122[sizeof("localtimestamp")]; char pgkw_str123[sizeof("precision")]; char pgkw_str125[sizeof("array")]; char pgkw_str126[sizeof("position")]; char pgkw_str127[sizeof("freeze")]; char pgkw_str128[sizeof("any")]; char pgkw_str129[sizeof("session_user")]; char pgkw_str130[sizeof("setof")]; char pgkw_str132[sizeof("decimal")]; char pgkw_str133[sizeof("xmlforest")]; char pgkw_str134[sizeof("asymmetric")]; char pgkw_str135[sizeof("xmlroot")]; char pgkw_str136[sizeof("xmlparse")]; char pgkw_str137[sizeof("current_time")]; char pgkw_str138[sizeof("xmlconcat")]; char pgkw_str139[sizeof("current_role")]; char pgkw_str140[sizeof("group")]; char pgkw_str142[sizeof("then")]; char pgkw_str144[sizeof("xmlpi")]; char pgkw_str145[sizeof("numeric")]; char pgkw_str146[sizeof("xmlelement")]; char pgkw_str147[sizeof("concurrently")]; char pgkw_str149[sizeof("false")]; char pgkw_str152[sizeof("over")]; char pgkw_str153[sizeof("xmlserialize")]; char pgkw_str154[sizeof("returning")]; char pgkw_str155[sizeof("using")]; char pgkw_str157[sizeof("bit")]; char pgkw_str160[sizeof("placing")]; char pgkw_str162[sizeof("between")]; char pgkw_str163[sizeof("bigint")]; char pgkw_str164[sizeof("primary")]; char pgkw_str165[sizeof("char")]; char pgkw_str166[sizeof("check")]; char pgkw_str168[sizeof("from")]; char pgkw_str170[sizeof("symmetric")]; char pgkw_str175[sizeof("authorization")]; char pgkw_str177[sizeof("verbose")]; char pgkw_str181[sizeof("timestamp")]; char pgkw_str183[sizeof("current_schema")]; char pgkw_str184[sizeof("full")]; char pgkw_str185[sizeof("foreign")]; char pgkw_str186[sizeof("xmlexists")]; char pgkw_str188[sizeof("interval")]; char pgkw_str192[sizeof("boolean")]; char pgkw_str198[sizeof("current_date")]; char pgkw_str200[sizeof("current_user")]; char pgkw_str202[sizeof("current_timestamp")]; char pgkw_str204[sizeof("when")]; char pgkw_str205[sizeof("where")]; char pgkw_str206[sizeof("character")]; char pgkw_str207[sizeof("off")]; char pgkw_str208[sizeof("overlaps")]; char pgkw_str213[sizeof("values")]; char pgkw_str218[sizeof("current_catalog")]; char pgkw_str219[sizeof("varchar")]; char pgkw_str220[sizeof("with")]; char pgkw_str224[sizeof("substring")]; char pgkw_str227[sizeof("window")]; char pgkw_str236[sizeof("fetch")]; char pgkw_str237[sizeof("initially")]; char pgkw_str265[sizeof("overlay")]; char pgkw_str266[sizeof("both")]; char pgkw_str272[sizeof("variadic")]; char pgkw_str273[sizeof("xmlattributes")]; char pgkw_str279[sizeof("nullif")]; char pgkw_str289[sizeof("having")]; char pgkw_str311[sizeof("binary")]; }; static const struct pgkw_t pgkw_contents = { "treat", "true", "or", "order", "not", "to", "left", "least", "real", "join", "on", "none", "else", "right", "select", "int", "time", "inout", "some", "inner", "limit", "in", "nchar", "into", "like", "ilike", "notnull", "table", "localtime", "integer", "cross", "create", "collate", "references", "is", "all", "analyze", "column", "intersect", "constraint", "except", "grant", "trim", "cast", "isnull", "as", "national", "coalesce", "case", "analyse", "row", "greatest", "end", "new", "out", "do", "asc", "old", "outer", "similar", "union", "default", "null", "user", "leading", "extract", "trailing", "only", "exists", "natural", "unique", "dec", "desc", "distinct", "deferrable", "and", "for", "float", "smallint", "offset", "localtimestamp", "precision", "array", "position", "freeze", "any", "session_user", "setof", "decimal", "xmlforest", "asymmetric", "xmlroot", "xmlparse", "current_time", "xmlconcat", "current_role", "group", "then", "xmlpi", "numeric", "xmlelement", "concurrently", "false", "over", "xmlserialize", "returning", "using", "bit", "placing", "between", "bigint", "primary", "char", "check", "from", "symmetric", "authorization", "verbose", "timestamp", "current_schema", "full", "foreign", "xmlexists", "interval", "boolean", "current_date", "current_user", "current_timestamp", "when", "where", "character", "off", "overlaps", "values", "current_catalog", "varchar", "with", "substring", "window", "fetch", "initially", "overlay", "both", "variadic", "xmlattributes", "nullif", "having", "binary" }; #define pgkw ((const char *) &pgkw_contents) static const int wordlist[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str16, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str22, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str24, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str27, (int)(long)&((struct pgkw_t *)0)->pgkw_str28, (int)(long)&((struct pgkw_t *)0)->pgkw_str29, (int)(long)&((struct pgkw_t *)0)->pgkw_str30, (int)(long)&((struct pgkw_t *)0)->pgkw_str31, (int)(long)&((struct pgkw_t *)0)->pgkw_str32, (int)(long)&((struct pgkw_t *)0)->pgkw_str33, (int)(long)&((struct pgkw_t *)0)->pgkw_str34, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str36, (int)(long)&((struct pgkw_t *)0)->pgkw_str37, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str39, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str41, (int)(long)&((struct pgkw_t *)0)->pgkw_str42, (int)(long)&((struct pgkw_t *)0)->pgkw_str43, (int)(long)&((struct pgkw_t *)0)->pgkw_str44, (int)(long)&((struct pgkw_t *)0)->pgkw_str45, (int)(long)&((struct pgkw_t *)0)->pgkw_str46, (int)(long)&((struct pgkw_t *)0)->pgkw_str47, (int)(long)&((struct pgkw_t *)0)->pgkw_str48, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str51, (int)(long)&((struct pgkw_t *)0)->pgkw_str52, (int)(long)&((struct pgkw_t *)0)->pgkw_str53, (int)(long)&((struct pgkw_t *)0)->pgkw_str54, (int)(long)&((struct pgkw_t *)0)->pgkw_str55, (int)(long)&((struct pgkw_t *)0)->pgkw_str56, (int)(long)&((struct pgkw_t *)0)->pgkw_str57, (int)(long)&((struct pgkw_t *)0)->pgkw_str58, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str60, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str62, (int)(long)&((struct pgkw_t *)0)->pgkw_str63, (int)(long)&((struct pgkw_t *)0)->pgkw_str64, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str66, (int)(long)&((struct pgkw_t *)0)->pgkw_str67, (int)(long)&((struct pgkw_t *)0)->pgkw_str68, (int)(long)&((struct pgkw_t *)0)->pgkw_str69, (int)(long)&((struct pgkw_t *)0)->pgkw_str70, (int)(long)&((struct pgkw_t *)0)->pgkw_str71, (int)(long)&((struct pgkw_t *)0)->pgkw_str72, (int)(long)&((struct pgkw_t *)0)->pgkw_str73, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str75, (int)(long)&((struct pgkw_t *)0)->pgkw_str76, (int)(long)&((struct pgkw_t *)0)->pgkw_str77, (int)(long)&((struct pgkw_t *)0)->pgkw_str78, (int)(long)&((struct pgkw_t *)0)->pgkw_str79, (int)(long)&((struct pgkw_t *)0)->pgkw_str80, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str83, (int)(long)&((struct pgkw_t *)0)->pgkw_str84, (int)(long)&((struct pgkw_t *)0)->pgkw_str85, (int)(long)&((struct pgkw_t *)0)->pgkw_str86, (int)(long)&((struct pgkw_t *)0)->pgkw_str87, (int)(long)&((struct pgkw_t *)0)->pgkw_str88, (int)(long)&((struct pgkw_t *)0)->pgkw_str89, (int)(long)&((struct pgkw_t *)0)->pgkw_str90, (int)(long)&((struct pgkw_t *)0)->pgkw_str91, (int)(long)&((struct pgkw_t *)0)->pgkw_str92, (int)(long)&((struct pgkw_t *)0)->pgkw_str93, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str95, (int)(long)&((struct pgkw_t *)0)->pgkw_str96, (int)(long)&((struct pgkw_t *)0)->pgkw_str97, (int)(long)&((struct pgkw_t *)0)->pgkw_str98, (int)(long)&((struct pgkw_t *)0)->pgkw_str99, (int)(long)&((struct pgkw_t *)0)->pgkw_str100, (int)(long)&((struct pgkw_t *)0)->pgkw_str101, (int)(long)&((struct pgkw_t *)0)->pgkw_str102, (int)(long)&((struct pgkw_t *)0)->pgkw_str103, (int)(long)&((struct pgkw_t *)0)->pgkw_str104, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str106, (int)(long)&((struct pgkw_t *)0)->pgkw_str107, (int)(long)&((struct pgkw_t *)0)->pgkw_str108, (int)(long)&((struct pgkw_t *)0)->pgkw_str109, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str111, (int)(long)&((struct pgkw_t *)0)->pgkw_str112, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str115, (int)(long)&((struct pgkw_t *)0)->pgkw_str116, (int)(long)&((struct pgkw_t *)0)->pgkw_str117, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str119, (int)(long)&((struct pgkw_t *)0)->pgkw_str120, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str122, (int)(long)&((struct pgkw_t *)0)->pgkw_str123, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str125, (int)(long)&((struct pgkw_t *)0)->pgkw_str126, (int)(long)&((struct pgkw_t *)0)->pgkw_str127, (int)(long)&((struct pgkw_t *)0)->pgkw_str128, (int)(long)&((struct pgkw_t *)0)->pgkw_str129, (int)(long)&((struct pgkw_t *)0)->pgkw_str130, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str132, (int)(long)&((struct pgkw_t *)0)->pgkw_str133, (int)(long)&((struct pgkw_t *)0)->pgkw_str134, (int)(long)&((struct pgkw_t *)0)->pgkw_str135, (int)(long)&((struct pgkw_t *)0)->pgkw_str136, (int)(long)&((struct pgkw_t *)0)->pgkw_str137, (int)(long)&((struct pgkw_t *)0)->pgkw_str138, (int)(long)&((struct pgkw_t *)0)->pgkw_str139, (int)(long)&((struct pgkw_t *)0)->pgkw_str140, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str142, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str144, (int)(long)&((struct pgkw_t *)0)->pgkw_str145, (int)(long)&((struct pgkw_t *)0)->pgkw_str146, (int)(long)&((struct pgkw_t *)0)->pgkw_str147, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str149, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str152, (int)(long)&((struct pgkw_t *)0)->pgkw_str153, (int)(long)&((struct pgkw_t *)0)->pgkw_str154, (int)(long)&((struct pgkw_t *)0)->pgkw_str155, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str157, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str160, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str162, (int)(long)&((struct pgkw_t *)0)->pgkw_str163, (int)(long)&((struct pgkw_t *)0)->pgkw_str164, (int)(long)&((struct pgkw_t *)0)->pgkw_str165, (int)(long)&((struct pgkw_t *)0)->pgkw_str166, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str168, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str170, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str175, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str177, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str181, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str183, (int)(long)&((struct pgkw_t *)0)->pgkw_str184, (int)(long)&((struct pgkw_t *)0)->pgkw_str185, (int)(long)&((struct pgkw_t *)0)->pgkw_str186, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str188, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str192, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str198, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str200, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str202, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str204, (int)(long)&((struct pgkw_t *)0)->pgkw_str205, (int)(long)&((struct pgkw_t *)0)->pgkw_str206, (int)(long)&((struct pgkw_t *)0)->pgkw_str207, (int)(long)&((struct pgkw_t *)0)->pgkw_str208, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str213, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str218, (int)(long)&((struct pgkw_t *)0)->pgkw_str219, (int)(long)&((struct pgkw_t *)0)->pgkw_str220, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str224, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str227, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str236, (int)(long)&((struct pgkw_t *)0)->pgkw_str237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str265, (int)(long)&((struct pgkw_t *)0)->pgkw_str266, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str272, (int)(long)&((struct pgkw_t *)0)->pgkw_str273, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str279, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str289, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str311 }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = pg_keyword_lookup_hash (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { register int o = wordlist[key]; if (o >= 0) { register const char *s = o + pgkw; if (*str == *s && !strcmp (str + 1, s + 1)) return s; } } } return 0; } pgqd/lib/usual/event.c0000664000401600040160000004066513175113172013253 0ustar cbecbe/* * event.c - libevent compatible event loop. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Small poll()-based async event loop, API-compatible with libevent. * * For sitations where full libevent is not necessary. */ #include #ifndef HAVE_LIBEVENT #include #include #include #include #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif /* max number of signals we care about */ #define MAX_SIGNAL 32 /* if tv_sec is larger, it's absolute timeout */ #define MAX_REL_TIMEOUT (30*24*60*60) /* if no nearby timeouts, max time to sleep (usecs) */ #define MAX_SLEEP (5*USEC) /* extra event flag to track if event is added */ #define EV_ACTIVE 0x80 struct event_base { /* pending timeouts */ struct Heap *timeout_heap; /* fd events */ struct StatList fd_list; /* pollfd <-> event mapping */ struct event **pfd_event; struct pollfd *pfd_list; int pfd_size; /* signal handling */ struct List sig_node; unsigned int sig_seen[MAX_SIGNAL]; struct List sig_waiters[MAX_SIGNAL]; int sig_send, sig_recv; struct event sig_ev; /* exit loop ASAP */ bool loop_break; /* finish current loop and exit */ bool loop_exit; /* cache if refreshed after each poll() */ usec_t cached_time; }; /* default event base */ static struct event_base *current_base; /* global signal data */ static volatile unsigned int sig_count[MAX_SIGNAL]; static bool signal_set_up[MAX_SIGNAL]; static struct sigaction old_handler[MAX_SIGNAL]; static LIST(sig_base_list); /* internal signal functions */ static bool sig_init(struct event_base *base, int sig); static void sig_close(struct event_base *base); /* * Debugging. */ #ifdef CASSERT #include #include static void base_dbg(struct event_base *base, const char *s, ...) { va_list ap; char buf[1024]; va_start(ap, s); vsnprintf(buf, sizeof(buf), s, ap); va_end(ap); log_noise("event base=%p: fdlist=%u timeouts=%d pfds=%d: %s", base, statlist_count(&base->fd_list), heap_size(base->timeout_heap), base->pfd_size, buf); } static void ev_dbg(struct event *ev, const char *s, ...) { va_list ap; char buf[1024], tval[128]; const char *typ = (ev->flags & EV_SIGNAL) ? "sig" : "fd"; va_start(ap, s); vsnprintf(buf, sizeof(buf), s, ap); va_end(ap); log_noise("event %s %d (flags=%s%s%s%s%s) [%s]: %s", typ, ev->fd, (ev->flags & EV_ACTIVE) ? "A" : "", (ev->flags & EV_PERSIST) ? "P" : "", (ev->flags & EV_TIMEOUT) ? "T" : "", (ev->flags & EV_READ) ? "R" : "", (ev->flags & EV_WRITE) ? "W" : "", (ev->flags & EV_TIMEOUT) ? format_time_ms(ev->timeout_val, tval, sizeof(tval)) : "-", buf); } #else #define base_dbg(b, ...) #define ev_dbg(b, ...) #endif /* * Helper functions. */ /* per-base time cache */ static usec_t get_base_time(struct event_base *base) { if (!base->cached_time) base->cached_time = get_time_usec(); return base->cached_time; } /* reset cached time */ static void reset_base_time(struct event_base *base) { base->cached_time = 0; } /* convert user tv to absolute tv */ static usec_t convert_timeout(struct event_base *base, struct timeval *tv) { usec_t val = tv->tv_sec * USEC + tv->tv_usec; if (tv->tv_sec < MAX_REL_TIMEOUT) val += get_base_time(base); return val; } static bool ev_is_better(const void *a, const void *b) { const struct event *ev1 = a, *ev2 = b; return ev1->timeout_val < ev2->timeout_val; } static void ev_save_pos(void *obj, unsigned pos) { struct event *ev = obj; ev->timeout_idx = pos; } /* enlarge pollfd array if needed */ static bool make_room(struct event_base *base, int need) { int total; void *tmp1; void *tmp2; if (need < base->pfd_size) return true; total = base->pfd_size * 2; if (total < 8) total = 8; while (total < need) total *= 2; tmp1 = realloc(base->pfd_list, total * sizeof(struct pollfd)); if (!tmp1) return false; base->pfd_list = tmp1; tmp2 = realloc(base->pfd_event, total * sizeof(struct event *)); if (!tmp2) return false; base->pfd_event = tmp2; base->pfd_size = total; return true; } /* * Single base functions. */ struct event_base *event_init(void) { struct event_base *base = event_base_new(); if (!current_base) current_base = base; return base; } int event_loop(int loop_flags) { return event_base_loop(current_base, loop_flags); } int event_loopbreak(void) { return event_base_loopbreak(current_base); } void event_set(struct event *ev, int fd, short flags, uevent_cb_f cb, void *arg) { event_assign(ev, current_base, fd, flags, cb, arg); } int event_once(int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout) { return event_base_once(current_base, fd, flags, cb_func, cb_arg, timeout); } int event_loopexit(struct timeval *timeout) { return event_base_loopexit(current_base, timeout); } /* * Event base initialization. */ struct event_base *event_base_new(void) { struct event_base *base; int i; base = calloc(1, sizeof(*base)); if (!base) return NULL; /* initialize timeout and fd areas */ base->timeout_heap = heap_create(ev_is_better, ev_save_pos, NULL); if (!base->timeout_heap) { free(base); return NULL; } statlist_init(&base->fd_list, "fd_list"); /* initialize signal areas */ for (i = 0; i < MAX_SIGNAL; i++) list_init(&base->sig_waiters[i]); list_init(&base->sig_node); base->sig_send = base->sig_recv = -1; /* allocate pollfds */ if (!make_room(base, 8)) { event_base_free(base); return NULL; } return base; } void event_base_free(struct event_base *base) { if (!base) { if (!current_base) return; base = current_base; } if (base == current_base) current_base = NULL; heap_destroy(base->timeout_heap); free(base->pfd_event); free(base->pfd_list); sig_close(base); free(base); } /* set flag to exit loop ASAP */ int event_base_loopbreak(struct event_base *base) { base->loop_break = true; return 0; } /* * Multi-base functions. */ /* fill event structure */ void event_assign(struct event *ev, struct event_base *base, int fd, short flags, uevent_cb_f cb, void *arg) { Assert(base); Assert((ev->flags & EV_ACTIVE) == 0); if (base == NULL) base = current_base; ev->fd = fd; ev->base = base; ev->flags = flags; ev->cb_func = cb; ev->cb_arg = arg; ev->ev_idx = -1; list_init(&ev->node); ev_dbg(ev, "event_set"); } /* Change base for a event */ int event_base_set(struct event_base *base, struct event *ev) { if (ev->flags & EV_ACTIVE) { errno = EINVAL; return -1; } ev->base = base; return 0; } /* Check if activated */ int is_event_active(struct event *ev) { return (ev->flags & EV_ACTIVE) ? 1 : 0; } /* de-activate event */ int event_del(struct event *ev) { struct event_base *base = ev->base; /* allow repeated deletions */ if ((ev->flags & EV_ACTIVE) == 0) { ev_dbg(ev, "event_del for inactive event??"); return 0; } ev_dbg(ev, "event_del"); /* remove from fd/signal list */ if (ev->flags & EV_SIGNAL) list_del(&ev->node); else if (ev->flags & (EV_READ | EV_WRITE)) statlist_remove(&base->fd_list, &ev->node); /* remove from timeout tree */ if (ev->flags & EV_TIMEOUT) { heap_remove(ev->base->timeout_heap, ev->timeout_idx); ev->flags &= ~EV_TIMEOUT; } /* clear reference to pollfd area */ if (ev->ev_idx >= 0) { ev->base->pfd_event[ev->ev_idx] = NULL; ev->ev_idx = -1; } /* tag inactive */ ev->flags &= ~EV_ACTIVE; return 0; } /* activate event */ int event_add(struct event *ev, struct timeval *timeout) { struct event_base *base = ev->base; Assert((ev->flags & EV_ACTIVE) == 0); Assert(base); /* sanity check, but dont do anything yet */ if (timeout) { if (ev->flags & EV_PERSIST) goto err_inval; if (!heap_reserve(base->timeout_heap, 1)) return -1; } else { if (ev->flags & EV_TIMEOUT) ev->flags &= ~EV_TIMEOUT; if (!(ev->flags & (EV_SIGNAL | EV_READ | EV_WRITE))) goto err_inval; } /* setup signal/fd */ if (ev->flags & EV_SIGNAL) { if (ev->flags & (EV_READ|EV_WRITE)) goto err_inval; if (!sig_init(base, ev->fd)) return -1; list_append(&base->sig_waiters[ev->fd], &ev->node); } else if (ev->flags & (EV_READ|EV_WRITE)) { statlist_append(&base->fd_list, &ev->node); } /* now act on timeout */ if (timeout) { ev->timeout_val = convert_timeout(base, timeout); ev->flags |= EV_TIMEOUT; heap_push(base->timeout_heap, ev); } ev->ev_idx = -1; ev->flags |= EV_ACTIVE; ev_dbg(ev, "event_add"); return 0; err_inval: errno = EINVAL; return -1; } /* * Event loop functions. */ static void deliver_event(struct event *ev, short flags) { ev_dbg(ev, "deliver_event: %d", flags); /* remove non-persitant event before calling user func */ if ((ev->flags & EV_PERSIST) == 0) event_del(ev); /* now call user func */ ev->cb_func(ev->fd, flags, ev->cb_arg); } static inline struct event *get_smallest_timeout(struct event_base *base) { return heap_top(base->timeout_heap); } /* decide how long poll() should sleep */ static int calc_timeout_ms(struct event_base *base) { struct event *ev; usec_t now; usec_t res; ev = get_smallest_timeout(base); if (!ev) return MAX_SLEEP / 1000; now = get_base_time(base); if (now + MAX_SLEEP < ev->timeout_val) res = MAX_SLEEP; else if (ev->timeout_val < now) res = 0; else res = ev->timeout_val - now; /* round up */ return (res + 999) / 1000; } /* deliver fd events */ static void process_fds(struct event_base *base, int pf_cnt) { int i; for (i = 0; i < pf_cnt; i++) { struct pollfd *pf = &base->pfd_list[i]; struct event *ev = base->pfd_event[i]; if (!ev) continue; base->pfd_event[i] = NULL; ev->ev_idx = -1; if (pf->revents & (POLLIN | POLLOUT | POLLERR | POLLHUP)) { int flags = ev->flags & (EV_READ | EV_WRITE); deliver_event(ev, flags); } if (base->loop_break) break; } } /* handle passed timeouts */ static void process_timeouts(struct event_base *base) { usec_t now; struct event *ev; ev = get_smallest_timeout(base); if (!ev) return; now = get_base_time(base); while (ev) { if (now < ev->timeout_val) break; deliver_event(ev, EV_TIMEOUT); if (base->loop_break) break; ev = get_smallest_timeout(base); } } /* main event loop */ int event_base_loop(struct event_base *base, int loop_flags) { int pf_cnt, res, timeout_ms; struct List *node; /* don't loop if non-block was requested */ if (loop_flags & EVLOOP_NONBLOCK) loop_flags |= EVLOOP_ONCE; base->loop_break = false; base->loop_exit = false; loop: if (!make_room(base, statlist_count(&base->fd_list))) return -1; /* fill pollfds */ pf_cnt = 0; statlist_for_each(node, &base->fd_list) { struct event *ev = container_of(node, struct event, node); struct pollfd *pf; ev->ev_idx = pf_cnt++; base->pfd_event[ev->ev_idx] = ev; pf = &base->pfd_list[ev->ev_idx]; pf->events = 0; pf->revents = 0; pf->fd = ev->fd; if (ev->flags & EV_READ) pf->events |= POLLIN; if (ev->flags & EV_WRITE) pf->events |= POLLOUT; } /* decide sleep time */ if (loop_flags & EVLOOP_NONBLOCK) timeout_ms = 0; else timeout_ms = calc_timeout_ms(base); /* forget cached time */ reset_base_time(base); /* poll for events */ res = poll(base->pfd_list, pf_cnt, timeout_ms); base_dbg(base, "poll(%d, timeout=%d) = res=%d errno=%d", pf_cnt, timeout_ms, res, res < 0 ? errno : 0); if (res == -1 && errno != EINTR) return -1; /* process events */ if (res > 0) { process_fds(base, pf_cnt); if (base->loop_break) return 0; } process_timeouts(base); /* decide whether to continue looping */ if (loop_flags & EVLOOP_ONCE) return 0; if (base->loop_break || base->loop_exit) return 0; goto loop; } /* * Signal handling. */ /* global signal handler registered via sigaction() */ static void uevent_sig_handler(int sig) { struct List *node, *tmp; struct event_base *base; uint8_t byte = sig; int res; if (sig < 0 || sig >= MAX_SIGNAL) return; sig_count[sig]++; list_for_each_safe(node, &sig_base_list, tmp) { base = container_of(node, struct event_base, sig_node); if (base->sig_send >= 0) { loop: res = send(base->sig_send, &byte, 1, MSG_NOSIGNAL); if (res == -1 && (errno == EINTR)) goto loop; } } } /* close signal resources on one base */ static void sig_close(struct event_base *base) { list_del(&base->sig_node); if (base->sig_send >= 0) close(base->sig_send); if (base->sig_recv >= 0) close(base->sig_recv); base->sig_recv = base->sig_send = -1; } /* call all handlers waiting for specific signal */ static void deliver_signal(struct event_base *base, int sig) { struct List *node, *tmp; list_for_each_safe(node, &base->sig_waiters[sig], tmp) { struct event *ev = container_of(node, struct event, node); deliver_event(ev, EV_SIGNAL); } } /* reader from sig socket, calls actual signal handlers */ static void sig_reader(int fd, short flags, void *arg) { struct event_base *base = arg; uint8_t buf[128]; int res, sig; /* drain the socket */ loop: res = recv(fd, buf, sizeof(buf), 0); if (res < 0) { if (errno == EINTR) goto loop; } else if ((res == sizeof(buf)) && (res > 1)) goto loop; /* now check for new signals */ for (sig = 0; sig < MAX_SIGNAL; sig++) { unsigned glob, local; if (list_empty(&base->sig_waiters[sig])) continue; glob = sig_count[sig]; local = base->sig_seen[sig]; if (glob != local) { base->sig_seen[sig] = glob; deliver_signal(base, sig); } } } /* setup signal handling for particular signal */ static bool sig_init(struct event_base *base, int sig) { int spair[2]; if (sig < 0 || sig >= MAX_SIGNAL) { errno = EINVAL; return false; } /* global handler setup */ if (!signal_set_up[sig]) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = uevent_sig_handler; sa.sa_flags = SA_RESTART; sigfillset(&sa.sa_mask); if (sigaction(sig, &sa, &old_handler[sig]) != 0) return false; } /* local handler for base */ if (list_empty(&base->sig_node)) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != 0) return false; if (!socket_setup(spair[0], true)) goto failed; if (!socket_setup(spair[1], true)) goto failed; event_assign(&base->sig_ev, base, spair[1], EV_READ|EV_PERSIST, sig_reader, base); if (event_add(&base->sig_ev, NULL) != 0) goto failed; base->sig_send = spair[0]; base->sig_recv = spair[1]; list_append(&sig_base_list, &base->sig_node); } /* if first waiter, then ignore previous signals */ if (list_empty(&base->sig_waiters[sig])) base->sig_seen[sig] = sig_count[sig]; return true; failed: close(spair[0]); close(spair[1]); return false; } /* * One-time events. */ struct once_event { struct event ev; uevent_cb_f cb_func; void *cb_arg; }; static void once_handler(int fd, short flags, void *arg) { struct once_event *once = arg; uevent_cb_f cb_func = once->cb_func; void *cb_arg = once->cb_arg; free(once); cb_func(fd, flags, cb_arg); } /* wait for one-time event, provide event struct internally */ int event_base_once(struct event_base *base, int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout) { struct once_event *once; if (flags & EV_PERSIST) { errno = EINVAL; return -1; } once = calloc(1, sizeof(*once)); if (!once) return -1; event_assign(&once->ev, base, fd, flags, once_handler, once); if (event_add(&once->ev, timeout) != 0) { free(once); return -1; } return 0; } /* * Stop loop at particular time. */ static void loopexit_handler(int fd, short flags, void *arg) { struct event_base *base = arg; base->loop_exit = true; } int event_base_loopexit(struct event_base *base, struct timeval *timeout) { if (!timeout) { errno = EINVAL; return -1; } return event_base_once(base, -1, 0, loopexit_handler, base, timeout); } /* * Info */ const char *event_get_version(void) { return "usual/event"; } const char *event_get_method(void) { return "poll"; } #endif /* !HAVE_LIBEVENT */ pgqd/lib/usual/pgutil.h0000664000401600040160000000276613175113172013443 0ustar cbecbe/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Utility functions for PostgreSQL data formats. */ #ifndef _USUAL_PGUTIL_H_ #define _USUAL_PGUTIL_H_ #include /** Check if string is reserver word for PostgreSQL. */ bool pg_is_reserved_word(const char *str); /** Quote value as string for PostgreSQL */ bool pg_quote_literal(char *_dst, const char *_src, int dstlen); /** Quote value as ident for PostgreSQL */ bool pg_quote_ident(char *_dst, const char *_src, int dstlen); /** Quote fully-qualified ident for PostgreSQL */ bool pg_quote_fqident(char *_dst, const char *_src, int dstlen); /** Parse PostgreSQL array. */ struct StrList *pg_parse_array(const char *pgarr, CxMem *cx); #endif pgqd/lib/usual/cbtree.c0000664000401600040160000001752613175113172013376 0ustar cbecbe/* * Crit-bit tree / binary radix tree. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Associates a C string with user pointer (called "obj"). * * Requires it's own internal nodes, thus not embeddable * to user structs. */ #include #include /* * - Childs are either other nodes or user pointers. * User pointers have lowest bit set. * * - All nodes have both childs. * * - Keys are handled as having infinite length, * zero-filled after actual end. */ struct Node { struct Node *child[2]; size_t bitpos; }; struct CBTree { struct Node *root; cbtree_getkey_func obj_key_cb; cbtree_walker_func obj_free_cb; void *cb_ctx; CxMem *cx; }; #define SAME_KEY SIZE_MAX #define MAX_KEY (SIZE_MAX / 8) /* * Low-level operations. */ /* does ptr point to user object or slot */ static inline bool is_node(void *ptr) { return ((uintptr_t)(ptr) & 1) == 0; } /* flag pointer as pointing to user object */ static inline void *set_external(const void *obj) { return (void*)((uintptr_t)(obj) | 1); } /* remove flag from user pointer */ static inline void *get_external(void *extval) { return (void*)((uintptr_t)(extval) & (~1)); } /* get specific bit from string */ static inline unsigned int get_bit(size_t bitpos, const unsigned char *key, size_t klen) { size_t pos = bitpos / 8; unsigned int bit = 7 - (bitpos % 8); return (pos < klen) && (key[pos] & (1 << bit)); } /* use callback to get key for a stored object */ static inline size_t get_key(struct CBTree *tree, void *obj, const void **key_p) { return tree->obj_key_cb(tree->cb_ctx, obj, key_p); } /* check if object key matches argument */ static inline bool key_matches(struct CBTree *tree, void *obj, const void *key, size_t klen) { const void *o_key; size_t o_klen; o_klen = get_key(tree, obj, &o_key); return (o_klen == klen) && (memcmp(key, o_key, klen) == 0); } /* Find first differing bit on 2 strings. */ static size_t find_crit_bit(const unsigned char *a, size_t alen, const unsigned char *b, size_t blen) { unsigned char av, bv, c, pos; size_t i; size_t minlen = (alen > blen) ? blen : alen; size_t maxlen = (alen > blen) ? alen : blen; /* find differing byte in common data */ for (i = 0; i < minlen; i++) { av = a[i]; bv = b[i]; if (av != bv) goto found; } /* find differing byte when one side is zero-filled */ for (; i < maxlen; i++) { av = (i < alen) ? a[i] : 0; bv = (i < blen) ? b[i] : 0; if (av != bv) goto found; } return SAME_KEY; found: /* calculate bits that differ */ c = av ^ bv; /* find the first one */ pos = 8 - fls(c); return i * 8 + pos; } /* * Lookup */ /* walk nodes until external pointer is found */ static void *raw_lookup(struct CBTree *tree, const void *key, size_t klen) { struct Node *node = tree->root; unsigned int bit; while (is_node(node)) { bit = get_bit(node->bitpos, key, klen); node = node->child[bit]; } return get_external(node); } /* actual lookup. returns obj ptr or NULL of not found */ void *cbtree_lookup(struct CBTree *tree, const void *key, size_t klen) { void *obj; if (!tree->root) return NULL; /* find match based on bits we know about */ obj = raw_lookup(tree, key, klen); /* need to check if the object actually matches */ if (key_matches(tree, obj, key, klen)) return obj; return NULL; } /* * Insertion. */ /* node allocation */ static struct Node *new_node(struct CBTree *tree) { struct Node *node = cx_alloc(tree->cx, sizeof(*node)); memset(node, 0, sizeof(*node)); return node; } /* insert into empty tree */ static bool insert_first(struct CBTree *tree, void *obj) { tree->root = set_external(obj); return true; } /* insert into specific bit-position */ static bool insert_at(struct CBTree *tree, size_t newbit, const void *key, size_t klen, void *obj) { /* location of current node/obj pointer under examination */ struct Node **pos = &tree->root; struct Node *node; unsigned int bit; while (is_node(*pos) && ((*pos)->bitpos < newbit)) { bit = get_bit((*pos)->bitpos, key, klen); pos = &(*pos)->child[bit]; } bit = get_bit(newbit, key, klen); node = new_node(tree); if (!node) return false; node->bitpos = newbit; node->child[bit] = set_external(obj); node->child[bit ^ 1] = *pos; *pos = node; return true; } /* actual insert: returns true -> insert ok or key found, false -> alloc failure */ bool cbtree_insert(struct CBTree *tree, void *obj) { const void *key, *old_key; size_t newbit, klen, old_klen; void *old_obj; if (!tree->root) return insert_first(tree, obj); /* current key */ klen = get_key(tree, obj, &key); if (klen > MAX_KEY) return false; /* nearest key in tree */ old_obj = raw_lookup(tree, key, klen); old_klen = get_key(tree, old_obj, &old_key); /* first differing bit is the target position */ newbit = find_crit_bit(key, klen, old_key, old_klen); if (newbit == SAME_KEY) return false; return insert_at(tree, newbit, key, klen, obj); } /* * Key deletion. */ /* true -> object was found and removed, false -> not found */ bool cbtree_delete(struct CBTree *tree, const void *key, size_t klen) { void *obj, *tmp; unsigned bit = 0; /* location of current node/obj pointer under examination */ struct Node **pos = &tree->root; /* if 'pos' has user obj, prev_pos has internal node pointing to it */ struct Node **prev_pos = NULL; if (!tree->root) return false; /* match bits we know about */ while (is_node(*pos)) { bit = get_bit((*pos)->bitpos, key, klen); prev_pos = pos; pos = &(*pos)->child[bit]; } /* does the key actually matches */ obj = get_external(*pos); if (!key_matches(tree, obj, key, klen)) return false; if (tree->obj_free_cb) tree->obj_free_cb(tree->cb_ctx, obj); /* drop the internal node pointing to our key */ if (prev_pos) { tmp = *prev_pos; *prev_pos = (*prev_pos)->child[bit ^ 1]; cx_free(tree->cx, tmp); } else { tree->root = NULL; } return true; } /* * Management. */ struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb, cbtree_walker_func obj_free_cb, void *cb_ctx, CxMem *cx) { struct CBTree *tree = cx_alloc(cx, sizeof(*tree)); if (!tree) return NULL; tree->root = NULL; tree->cb_ctx = cb_ctx; tree->obj_key_cb = obj_key_cb; tree->obj_free_cb = obj_free_cb; tree->cx = cx; return tree; } /* recursive freeing */ static void destroy_node(struct CBTree *tree, struct Node *node) { if (is_node(node)) { destroy_node(tree, node->child[0]); destroy_node(tree, node->child[1]); cx_free(tree->cx, node); } else if (tree->obj_free_cb) { void *obj = get_external(node); tree->obj_free_cb(tree->cb_ctx, obj); } } /* Free tree and all it's internal nodes. */ void cbtree_destroy(struct CBTree *tree) { if (tree->root) destroy_node(tree, tree->root); tree->root = NULL; cx_free(tree->cx, tree); } /* * walk over tree */ static bool walk(struct Node *node, cbtree_walker_func cb_func, void *cb_arg) { if (!is_node(node)) return cb_func(cb_arg, get_external(node)); return walk(node->child[0], cb_func, cb_arg) && walk(node->child[1], cb_func, cb_arg); } bool cbtree_walk(struct CBTree *tree, cbtree_walker_func cb_func, void *cb_arg) { if (!tree->root) return true; return walk(tree->root, cb_func, cb_arg); } pgqd/lib/usual/hashing/0000775000401600040160000000000013175113172013374 5ustar cbecbepgqd/lib/usual/hashing/spooky.h0000664000401600040160000000045113175113172015071 0ustar cbecbe/** * @file * * Jenkins SpookyHash V2 - fast hash for 64-bit CPUs. */ #ifndef _USUAL_HASHING_SPOOKY_H_ #define _USUAL_HASHING_SPOOKY_H_ #include /** * Run SpookyHash on data. */ void spookyhash(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2); #endif pgqd/lib/usual/hashing/xxhash.c0000664000401600040160000000547313175113172015054 0ustar cbecbe/* xxHash - Fast Hash algorithm Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ #include #include #include #define PRIME32_1 2654435761U #define PRIME32_2 2246822519U #define PRIME32_3 3266489917U #define PRIME32_4 668265263U #define PRIME32_5 374761393U #define read32(p) h32dec(p) uint32_t xxhash(const void *input, size_t len, uint32_t seed) { const uint8_t *p = (const uint8_t *)input; const uint8_t * const bEnd = p + len; uint32_t h32; if (len >= 16) { const uint8_t * const limit = bEnd - 16; uint32_t v1, v2, v3, v4; v1 = seed + PRIME32_1 + PRIME32_2; v2 = seed + PRIME32_2; v3 = seed + 0; v4 = seed - PRIME32_1; do { v1 += read32(p) * PRIME32_2; v1 = rol32(v1, 13); v1 *= PRIME32_1; p += 4; v2 += read32(p) * PRIME32_2; v2 = rol32(v2, 13); v2 *= PRIME32_1; p += 4; v3 += read32(p) * PRIME32_2; v3 = rol32(v3, 13); v3 *= PRIME32_1; p += 4; v4 += read32(p) * PRIME32_2; v4 = rol32(v4, 13); v4 *= PRIME32_1; p += 4; } while (p <= limit); h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18); } else { h32 = seed + PRIME32_5; } h32 += len; while (p <= bEnd - 4) { h32 += read32(p) * PRIME32_3; h32 = rol32(h32, 17) * PRIME32_4 ; p += 4; } while (p < bEnd) { h32 += (*p) * PRIME32_5; h32 = rol32(h32, 11) * PRIME32_1 ; p++; } h32 ^= h32 >> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } pgqd/lib/usual/hashing/memhash.h0000664000401600040160000000255113175113172015172 0ustar cbecbe/* * memhash.h - Randomized in-memory hashing. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Randomized in-memory hashing. * * Functions here use randomized seed and pick fastest hash * for current CPU. */ #ifndef _USUAL_HASHING_MEMHASH_H_ #define _USUAL_HASHING_MEMHASH_H_ #include /** * Hash data. */ uint32_t memhash(const void *data, size_t len); /** * Hash zero-terminated string. */ uint32_t memhash_string(const char *s); /** * Hash with given seed. Result is not randomized, * but still may vary on different CPU-s. */ uint32_t memhash_seed(const void *data, size_t len, uint32_t seed); #endif pgqd/lib/usual/hashing/lookup3.h0000664000401600040160000000040613175113172015141 0ustar cbecbe/** * @file * * Jenkins' lookup3 non-cryptographic hash. */ #ifndef _USUAL_HASHING_LOOKUP3_H_ #define _USUAL_HASHING_LOOKUP3_H_ #include /** * Calculate 64-bit hash over data */ uint64_t hash_lookup3(const void *data, size_t len); #endif pgqd/lib/usual/hashing/spooky.c0000664000401600040160000002504113175113172015066 0ustar cbecbe/* * SpookyHash: a 128-bit noncryptographic hash function * By Bob Jenkins, public domain * Oct 31 2010: alpha, framework + SpookyHash::Mix appears right * Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right * Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas * Feb 2 2012: production, same bits as beta * Feb 5 2012: adjusted definitions of uint* to be more portable * Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. * August 5 2012: SpookyV2 (different results) * * Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. * All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. * * This was developed for and tested on 64-bit x86-compatible processors. * It assumes the processor is little-endian. There is a macro * controlling whether unaligned reads are allowed (by default they are). * This should be an equally good hash on big-endian machines, but it will * compute different results on them than on little-endian machines. * * Google's CityHash has similar specs to SpookyHash, and CityHash is faster * on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders * of magnitude slower. CRCs are two or more times slower, but unlike * SpookyHash, they have nice math for combining the CRCs of pieces to form * the CRCs of wholes. There are also cryptographic hashes, but those are even * slower than MD5. */ #include #include #include #ifdef WORDS_UNALIGNED_ACCESS_OK #define ALLOW_UNALIGNED_READS 1 #else #define ALLOW_UNALIGNED_READS 0 #endif /* number of uint64_t's in internal state */ #define sc_numVars 12 /* size of the internal state */ #define sc_blockSize (sc_numVars*8) /* size of buffer of unhashed data, in bytes */ #define sc_bufSize (2*sc_blockSize) /* * sc_const: a constant which: * - is not zero * - is odd * - is a not-very-regular mix of 1's and 0's * - does not need any other special mathematical properties */ static const uint64_t sc_const = 0xdeadbeefdeadbeefLL; /* * This is used if the input is 96 bytes long or longer. * * The internal state is fully overwritten every 96 bytes. * Every input bit appears to cause at least 128 bits of entropy * before 96 other bytes are combined, when run forward or backward * For every input bit, * Two inputs differing in just that input bit * Where "differ" means xor or subtraction * And the base value is random * When run forward or backwards one Mix * I tried 3 pairs of each; they all differed by at least 212 bits. */ #define Mix(data, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11) \ do { \ s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = rol64(s0,11); s11 += s1; \ s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = rol64(s1,32); s0 += s2; \ s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = rol64(s2,43); s1 += s3; \ s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = rol64(s3,31); s2 += s4; \ s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = rol64(s4,17); s3 += s5; \ s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = rol64(s5,28); s4 += s6; \ s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = rol64(s6,39); s5 += s7; \ s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = rol64(s7,57); s6 += s8; \ s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = rol64(s8,55); s7 += s9; \ s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = rol64(s9,54); s8 += s10; \ s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = rol64(s10,22); s9 += s11; \ s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = rol64(s11,46); s10 += s0; \ } while (0) /* * Mix all 12 inputs together so that h0, h1 are a hash of them all. * * For two inputs differing in just the input bits * Where "differ" means xor or subtraction * And the base value is random, or a counting value starting at that bit * The final result will have each bit of h0, h1 flip * For every input bit, * with probability 50 +- .3% * For every pair of input bits, * with probability 50 +- 3% * * This does not rely on the last Mix() call having already mixed some. * Two iterations was almost good enough for a 64-bit result, but a * 128-bit result is reported, so End() does three iterations. */ #define EndPartial(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \ do { \ h11+= h1; h2 ^= h11; h1 = rol64(h1,44); \ h0 += h2; h3 ^= h0; h2 = rol64(h2,15); \ h1 += h3; h4 ^= h1; h3 = rol64(h3,34); \ h2 += h4; h5 ^= h2; h4 = rol64(h4,21); \ h3 += h5; h6 ^= h3; h5 = rol64(h5,38); \ h4 += h6; h7 ^= h4; h6 = rol64(h6,33); \ h5 += h7; h8 ^= h5; h7 = rol64(h7,10); \ h6 += h8; h9 ^= h6; h8 = rol64(h8,13); \ h7 += h9; h10^= h7; h9 = rol64(h9,38); \ h8 += h10; h11^= h8; h10= rol64(h10,53); \ h9 += h11; h0 ^= h9; h11= rol64(h11,42); \ h10+= h0; h1 ^= h10; h0 = rol64(h0,54); \ } while (0) #define End(data, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \ do { \ h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3]; \ h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7]; \ h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11]; \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ } while (0) /* * The goal is for each bit of the input to expand into 128 bits of * apparent entropy before it is fully overwritten. * n trials both set and cleared at least m bits of h0 h1 h2 h3 * n: 2 m: 29 * n: 3 m: 46 * n: 4 m: 57 * n: 5 m: 107 * n: 6 m: 146 * n: 7 m: 152 * when run forwards or backwards * for all 1-bit and 2-bit diffs * with diffs defined by either xor or subtraction * with a base of all zeros plus a counter, or plus another bit, or random */ #define ShortMix(h0, h1, h2, h3) \ do { \ h2 = rol64(h2,50); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,52); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,30); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,41); h1 += h2; h3 ^= h1; \ h2 = rol64(h2,54); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,48); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,38); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,37); h1 += h2; h3 ^= h1; \ h2 = rol64(h2,62); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,34); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,5); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,36); h1 += h2; h3 ^= h1; \ } while (0) /* * Mix all 4 inputs together so that h0, h1 are a hash of them all. * * For two inputs differing in just the input bits * Where "differ" means xor or subtraction * And the base value is random, or a counting value starting at that bit * The final result will have each bit of h0, h1 flip * For every input bit, * with probability 50 +- .3% (it is probably better than that) * For every pair of input bits, * with probability 50 +- .75% (the worst case is approximately that) */ #define ShortEnd(h0, h1, h2, h3) \ do { \ h3 ^= h2; h2 = rol64(h2,15); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,52); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,26); h1 += h0; \ h2 ^= h1; h1 = rol64(h1,51); h2 += h1; \ h3 ^= h2; h2 = rol64(h2,28); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,9); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,47); h1 += h0; \ h2 ^= h1; h1 = rol64(h1,54); h2 += h1; \ h3 ^= h2; h2 = rol64(h2,32); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,25); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,63); h1 += h0; \ } while (0) /* * Short is used for messages under 192 bytes in length * Short has a low startup cost, the normal mode is good for long * keys, the cost crossover is at about 192 bytes. The two modes were * held to the same quality bar. */ static void Short(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2) { uint64_t buf[2*sc_numVars]; union { const uint8_t *p8; uint32_t *p32; uint64_t *p64; size_t i; } u; size_t remainder = length%32; uint64_t a=*hash1; uint64_t b=*hash2; uint64_t c=sc_const; uint64_t d=sc_const; u.p8 = (const uint8_t *)message; if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) { memcpy(buf, message, length); u.p64 = buf; } if (length > 15) { const uint64_t *end = u.p64 + (length/32)*4; /* handle all complete sets of 32 bytes */ for (; u.p64 < end; u.p64 += 4) { c += u.p64[0]; d += u.p64[1]; ShortMix(a,b,c,d); a += u.p64[2]; b += u.p64[3]; } /* Handle the case of 16+ remaining bytes. */ if (remainder >= 16) { c += u.p64[0]; d += u.p64[1]; ShortMix(a,b,c,d); u.p64 += 2; remainder -= 16; } } /* Handle the last 0..15 bytes, and its length */ d += ((uint64_t)length) << 56; switch (remainder) { case 15: d += ((uint64_t)u.p8[14]) << 48; case 14: d += ((uint64_t)u.p8[13]) << 40; case 13: d += ((uint64_t)u.p8[12]) << 32; case 12: d += u.p32[2]; c += u.p64[0]; break; case 11: d += ((uint64_t)u.p8[10]) << 16; case 10: d += ((uint64_t)u.p8[9]) << 8; case 9: d += (uint64_t)u.p8[8]; case 8: c += u.p64[0]; break; case 7: c += ((uint64_t)u.p8[6]) << 48; case 6: c += ((uint64_t)u.p8[5]) << 40; case 5: c += ((uint64_t)u.p8[4]) << 32; case 4: c += u.p32[0]; break; case 3: c += ((uint64_t)u.p8[2]) << 16; case 2: c += ((uint64_t)u.p8[1]) << 8; case 1: c += (uint64_t)u.p8[0]; break; case 0: c += sc_const; d += sc_const; } ShortEnd(a,b,c,d); *hash1 = a; *hash2 = b; } /* do the whole hash in one call */ void spookyhash(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2) { uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; uint64_t buf[sc_numVars]; uint64_t *end; union { const uint8_t *p8; uint64_t *p64; } u; size_t remainder; if (length < sc_bufSize) { Short(message, length, hash1, hash2); return; } h0 = h3 = h6 = h9 = *hash1; h1 = h4 = h7 = h10 = *hash2; h2 = h5 = h8 = h11 = sc_const; u.p8 = (const uint8_t *)message; end = u.p64 + (length/sc_blockSize)*sc_numVars; /* handle all whole sc_blockSize blocks of bytes */ if (ALLOW_UNALIGNED_READS || (((uintptr_t)u.p8 & 0x7) == 0)) { while (u.p64 < end) { Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); u.p64 += sc_numVars; } } else { while (u.p64 < end) { memcpy(buf, u.p64, sc_blockSize); Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); u.p64 += sc_numVars; } } /* handle the last partial block of sc_blockSize bytes */ remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); memcpy(buf, end, remainder); memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); ((uint8_t *)buf)[sc_blockSize-1] = remainder; /* do some final mixing */ End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); *hash1 = h0; *hash2 = h1; } pgqd/lib/usual/hashing/xxhash.h0000664000401600040160000000355713175113172015062 0ustar cbecbe/* xxHash - Fast Hash algorithm Header File Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ /** * @file * * xxHash - fast hash for 32-bit cpu's. */ #ifndef _USUAL_HASHING_XXHASH_H_ #define _USUAL_HASHING_XXHASH_H_ #include /** * Calculate the 32-bits hash of sequence of length "len" stored at memory address "input". */ uint32_t xxhash(const void *input, size_t len, uint32_t seed); #endif pgqd/lib/usual/hashing/siphash.c0000664000401600040160000000500013175113172015172 0ustar cbecbe/* * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define SIP_ROUND1 \ v0 += v1; v1 = rol64(v1, 13); v1 ^= v0; v0 = rol64(v0, 32); \ v2 += v3; v3 = rol64(v3, 16); v3 ^= v2; \ v0 += v3; v3 = rol64(v3, 21); v3 ^= v0; \ v2 += v1; v1 = rol64(v1, 17); v1 ^= v2; v2 = rol64(v2, 32) #define SIP_ROUND2 SIP_ROUND1; SIP_ROUND1 #define SIP_ROUND4 SIP_ROUND2; SIP_ROUND2 #define SIP_ROUNDS(n) SIP_ROUND ## n #define sip_compress(n) \ do { \ v3 ^= m; \ SIP_ROUNDS(n); \ v0 ^= m; \ } while (0) #define sip_finalize(n) \ do { \ v2 ^= 0xff; \ SIP_ROUNDS(n); \ } while (0) uint64_t siphash24(const void *data, size_t len, uint64_t k0, uint64_t k1) { const uint8_t *s = data; const uint8_t *end = s + len - (len % 8); uint64_t v0 = k0 ^ UINT64_C(0x736f6d6570736575); uint64_t v1 = k1 ^ UINT64_C(0x646f72616e646f6d); uint64_t v2 = k0 ^ UINT64_C(0x6c7967656e657261); uint64_t v3 = k1 ^ UINT64_C(0x7465646279746573); uint64_t m; for (; s < end; s += 8) { m = le64dec(s); sip_compress(2); } m = (uint64_t)len << 56; switch (len & 7) { case 7: m |= (uint64_t)s[6] << 48; case 6: m |= (uint64_t)s[5] << 40; case 5: m |= (uint64_t)s[4] << 32; case 4: m |= (uint64_t)s[3] << 24; case 3: m |= (uint64_t)s[2] << 16; case 2: m |= (uint64_t)s[1] << 8; case 1: m |= (uint64_t)s[0]; break; case 0: break; } sip_compress(2); sip_finalize(4); return (v0 ^ v1 ^ v2 ^ v3); } uint64_t siphash24_secure(const void *data, size_t len) { static bool initialized; static uint64_t k0, k1; if (!initialized) { k0 = ((uint64_t)csrandom() << 32) | csrandom(); k1 = ((uint64_t)csrandom() << 32) | csrandom(); initialized = true; } return siphash24(data, len, k0, k1); } pgqd/lib/usual/hashing/memhash.c0000664000401600040160000000303613175113172015164 0ustar cbecbe/* * memhash.h - Randomized in-memory hashing. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include uint32_t memhash_seed(const void *data, size_t len, uint32_t seed) { if (sizeof(void *) == 8 || sizeof(long) == 8) { uint64_t hash[2]; hash[0] = seed; hash[1] = 0; spookyhash(data, len, &hash[0], &hash[1]); return hash[0]; } else { return xxhash(data, len, seed); } } uint32_t memhash(const void *data, size_t len) { static bool initialized; static uint32_t rand_seed; if (!initialized) { initialized = true; rand_seed = csrandom(); } return memhash_seed(data, len, rand_seed); } uint32_t memhash_string(const char *s) { return memhash(s, strlen(s)); } pgqd/lib/usual/hashing/lookup3.c0000664000401600040160000000270613175113172015141 0ustar cbecbe/* * The contents of this file are public domain. * * Based on: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */ /* * Compact version of Bob Jenkins' lookup3.c hash. */ #include #include #define rot(x, k) (((x)<<(k)) | ((x)>>(32-(k)))) #define mix(a, b, c) do { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } while (0) #define final(a, b, c) do { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c, 4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } while (0) /* variable length copy of ~6 bytes, avoid call to libc */ static inline void simple_memcpy(void *dst_, const void *src_, size_t len) { const uint8_t *src = src_; uint8_t *dst = dst_; while (len--) *dst++ = *src++; } uint64_t hash_lookup3(const void *data, size_t len) { uint32_t a, b, c; uint32_t buf[3]; const uint8_t *p = data; a = b = c = 0xdeadbeef + len; if (len == 0) goto done; while (len > 12) { memcpy(buf, p, 12); a += buf[0]; b += buf[1]; c += buf[2]; mix(a, b, c); p += 12; len -= 12; } buf[0] = buf[1] = buf[2] = 0; simple_memcpy(buf, p, len); a += buf[0]; b += buf[1]; c += buf[2]; final(a, b, c); done: return ((uint64_t)b << 32) | c; } pgqd/lib/usual/hashing/siphash.h0000664000401600040160000000207513175113172015210 0ustar cbecbe/* * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SipHash-2-4 */ #ifndef _USUAL_HASHING_SIPHASH_H_ #define _USUAL_HASHING_SIPHASH_H_ #include /** Calculate SipHash-2-4 checksum */ uint64_t siphash24(const void *data, size_t len, uint64_t k0, uint64_t k1); uint64_t siphash24_secure(const void *data, size_t len); #endif pgqd/lib/usual/hashing/crc32.h0000664000401600040160000000176313175113172014470 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * CRC32 checksum. */ #ifndef _USUAL_HASHING_CRC32_H_ #define _USUAL_HASHING_CRC32_H_ #include /** Calculate CRC32 checksum */ uint32_t calc_crc32(const void *data, size_t len, uint32_t init); #endif pgqd/lib/usual/hashing/crc32.c0000664000401600040160000001021113175113172014447 0ustar cbecbe/* * CRC32. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include static const uint32_t crc_tab[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; static inline uint32_t crc32(uint32_t prev, uint8_t c) { return crc_tab[(prev ^ c) & 0xFF] ^ (prev >> 8); } uint32_t calc_crc32(const void *data, size_t len, uint32_t init) { const uint8_t *p = data; uint32_t crc = init ^ (~0); while (len--) crc = crc32(crc, *p++); return crc ^ (~0); } pgqd/lib/usual/list.c0000664000401600040160000000373313175113172013100 0ustar cbecbe/* * Circular doubly linked list implementation. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * 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 /* merge 2 ordered arrays into one */ static struct List *merge(list_cmp_f cmp_func, struct List *p, struct List *q) { struct List res[1], *tail = res, *e; while (p && q) { if (cmp_func(p, q) <= 0) { e = p; p = p->next; } else { e = q; q = q->next; } tail->next = e; tail = e; } tail->next = p ? p : q; return res->next; } /* * non-recursive merge sort * * uses singly-linked NULL-terminated arrays internally. */ void list_sort(struct List *list, list_cmp_f cmp_func) { int i, top = 0; struct List *p; struct List *stack[64]; if (list_empty(list)) return; /* merge small sorted fragments into larger ones */ while (list->next != list) { p = list->next; list->next = p->next; p->next = NULL; for (i = 0; (i < top) && stack[i]; i++) { p = merge(cmp_func, stack[i], p); stack[i] = NULL; } stack[i] = p; if (i == top) top++; } /* merge remaining fragments */ for (p = NULL, i = 0; i < top; i++) p = merge(cmp_func, stack[i], p); /* restore proper List */ list->next = p; for (p = list; p->next; p = p->next) p->next->prev = p; list->prev = p; p->next = list; } pgqd/lib/usual/cbtree.h0000664000401600040160000000441113175113172013370 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Crit-bit tree / binary radix tree. */ #ifndef _USUAL_CBTREE_H_ #define _USUAL_CBTREE_H_ #include /** returns length of the key */ typedef size_t (*cbtree_getkey_func)(void *ctx, void *obj, const void **dst_p); /** walk over tree */ typedef bool (*cbtree_walker_func)(void *ctx, void *obj); /** Handle to tree */ struct CBTree; /** * Create new tree. * * @param obj_key_cb callback to get the key for a object * @param obj_free_cb callback to free the object when tree node is freed (optional) * @param cb_ctx extra pointer passed to callbacks * @param cx memory context where from allocate */ struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb, cbtree_walker_func obj_free_cb, void *cb_ctx, CxMem *cx); /** * frees all resources allocated. * If obj_free_cb is non-NULL, it will be called per each object. */ void cbtree_destroy(struct CBTree *tree); /** Inserts new node to tree */ bool cbtree_insert(struct CBTree *tree, void *obj) _MUSTCHECK; /** Removed node from tree. * If obj_free_cb is non-NULL, it will be called for the object. * * @returns true if key was found, false otherwise. */ bool cbtree_delete(struct CBTree *tree, const void *key, size_t klen); /** * Lookup a key. * * @returns object pointer if found, NULL ohterwise */ void *cbtree_lookup(struct CBTree *tree, const void *key, size_t klen); /** Walk over tree */ bool cbtree_walk(struct CBTree *tree, cbtree_walker_func cb_func, void *cb_arg); #endif pgqd/lib/usual/daemon.c0000664000401600040160000001076313175113172013371 0ustar cbecbe/* * Daemonization & pidfile handling. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include /* * pidfile management. */ static char *g_pidfile; static void remove_pidfile(void) { if (!g_pidfile) return; unlink(g_pidfile); free(g_pidfile); g_pidfile = NULL; } /* * Reads pid from pidfile and sends a signal to it. * * true - signaling was successful. * false - ENOENT / ESRCH * * fatal() otherwise. */ bool signal_pidfile(const char *pidfile, int sig) { char buf[128 + 1]; struct stat st; pid_t pid = 0; int fd, res; if (!pidfile || !pidfile[0]) return false; intr_loop: /* check if pidfile exists */ if (stat(pidfile, &st) < 0) goto fail; /* read old pid */ fd = open(pidfile, O_RDONLY); if (fd < 0) goto fail; res = read(fd, buf, sizeof(buf) - 1); close(fd); if (res <= 0) goto fail; /* parse pid */ buf[res] = 0; errno = 0; pid = strtoul(buf, NULL, 10); if (errno) { /* should we panic, or say no such process exists? */ if (0) errno = ESRCH; goto fail; } /* send the signal */ res = kill(pid, sig); if (res == 0) return true; fail: /* decide error seriousness */ if (errno == EINTR) goto intr_loop; if (errno == ENOENT || errno == ESRCH) return false; fatal_perror("signal_pidfile: Unexpected error"); } static void check_pidfile(const char *pidfile) { if (signal_pidfile(pidfile, 0)) fatal("pidfile exists, another instance running?"); if (errno == ESRCH) { log_info("Stale pidfile, removing"); unlink(pidfile); } } static void write_pidfile(const char *pidfile, bool first_write) { char buf[64]; pid_t pid; int res, fd, len; static int atexit_hook = 0; int flags = O_WRONLY | O_CREAT; if (!pidfile || !pidfile[0]) return; if (g_pidfile) free(g_pidfile); g_pidfile = strdup(pidfile); if (!g_pidfile) fatal_perror("cannot alloc pidfile"); pid = getpid(); snprintf(buf, sizeof(buf), "%u\n", (unsigned)pid); /* don't allow overwrite on first write */ if (first_write) flags |= O_EXCL; fd = open(pidfile, flags, 0644); if (fd < 0) fatal_perror("Cannot write pidfile: '%s'", pidfile); len = strlen(buf); loop: res = write(fd, buf, len); if (res < 0) { if (errno == EINTR) goto loop; fatal_perror("Write to pidfile failed: '%s'", pidfile); } else if (res < len) { len -= res; goto loop; } close(fd); if (!atexit_hook) { /* only remove when we have it actually written */ atexit(remove_pidfile); atexit_hook = 1; } } /* * Function: daemonize * * Handle pidfile and daemonization. * * If pidfile is given, check if old process is running. * * If going background is required, require non-empty pidfile * and logfile. Then fork to background and write pidfile. */ void daemonize(const char *pidfile, bool go_background) { int pid, fd; if (pidfile && pidfile[0]) { check_pidfile(pidfile); /* write pidfile twice, to be able to show problems to user */ write_pidfile(pidfile, true); } else if (go_background) fatal("daemon needs pidfile configured"); if (!go_background) return; if ((!cf_logfile || !cf_logfile[0]) && !cf_syslog) fatal("daemon needs logging configured"); /* send stdin, stdout, stderr to /dev/null */ fd = open("/dev/null", O_RDWR); if (fd < 0) fatal_perror("/dev/null"); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); /* fork new process */ pid = fork(); if (pid < 0) fatal_perror("fork"); if (pid > 0) _exit(0); /* create new session */ pid = setsid(); if (pid < 0) fatal_perror("setsid"); /* fork again to avoid being session leader */ pid = fork(); if (pid < 0) fatal_perror("fork"); if (pid > 0) _exit(0); write_pidfile(pidfile, false); } pgqd/lib/usual/tls/0000775000401600040160000000000013175113172012555 5ustar cbecbepgqd/lib/usual/tls/tls_peer.c0000664000401600040160000000401613175113172014537 0ustar cbecbe/* $OpenBSD: tls_peer.c,v 1.3 2015/09/11 13:22:39 beck Exp $ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include "tls_internal.h" const char * tls_peer_cert_hash(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->hash); return NULL; } const char * tls_peer_cert_issuer(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->issuer); return NULL; } const char * tls_peer_cert_subject(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->subject); return NULL; } int tls_peer_cert_provided(struct tls *ctx) { return (ctx->ssl_peer_cert != NULL); } int tls_peer_cert_contains_name(struct tls *ctx, const char *name) { if (ctx->ssl_peer_cert == NULL) return (0); return (tls_check_name(ctx, ctx->ssl_peer_cert, name) == 0); } time_t tls_peer_cert_notbefore(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notbefore); } time_t tls_peer_cert_notafter(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notafter); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls.h0000664000401600040160000001476513175113172013545 0ustar cbecbe/* $OpenBSD: tls.h,v 1.12 2015/03/31 14:03:38 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 _USUAL_HEADER_TLS_H_ #define _USUAL_HEADER_TLS_H_ #ifdef __cplusplus extern "C" { #endif #include #define TLS_API 20141031 #define TLS_PROTOCOL_TLSv1_0 (1 << 1) #define TLS_PROTOCOL_TLSv1_1 (1 << 2) #define TLS_PROTOCOL_TLSv1_2 (1 << 3) #define TLS_PROTOCOL_TLSv1 \ (TLS_PROTOCOL_TLSv1_0|TLS_PROTOCOL_TLSv1_1|TLS_PROTOCOL_TLSv1_2) #define TLS_PROTOCOLS_ALL TLS_PROTOCOL_TLSv1 #define TLS_PROTOCOLS_DEFAULT TLS_PROTOCOL_TLSv1_2 #define TLS_WANT_POLLIN -2 #define TLS_WANT_POLLOUT -3 #define TLS_NO_OCSP -4 #define TLS_NO_CERT -5 #define TLS_OCSP_RESPONSE_SUCCESSFUL 0 #define TLS_OCSP_RESPONSE_MALFORMED 1 #define TLS_OCSP_RESPONSE_INTERNALERR 2 #define TLS_OCSP_RESPONSE_TRYLATER 3 #define TLS_OCSP_RESPONSE_SIGREQUIRED 5 #define TLS_OCSP_RESPONSE_UNAUTHORIZED 6 #define TLS_OCSP_CERT_GOOD 0 #define TLS_OCSP_CERT_REVOKED 1 #define TLS_OCSP_CERT_UNKNOWN 2 #define TLS_CRL_REASON_UNPSECIFIED 0 #define TLS_CRL_REASON_KEY_COMPROMISE 1 #define TLS_CRL_REASON_CA_COMPROMISE 2 #define TLS_CRL_REASON_AFFILIATION_CHANGED 3 #define TLS_CRL_REASON_SUPERSEDED 4 #define TLS_CRL_REASON_CESSATION_OF_OPERATION 5 #define TLS_CRL_REASON_CERTIFICATE_HOLD 6 #define TLS_CRL_REASON_REMOVE_FROM_CRL 8 #define TLS_CRL_REASON_PRIVILEGE_WITH_DRAWN 9 #define TLS_CRL_REASON_AA_COMPROMISE 10 struct tls; struct tls_config; int tls_init(void); void tls_deinit(void); const char *tls_backend_version(void); const char *tls_error(struct tls *_ctx); struct tls_config *tls_config_new(void); void tls_config_free(struct tls_config *_config); int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file); int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path); int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, size_t _len); int tls_config_set_cert_file(struct tls_config *_config, const char *_cert_file); int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert, size_t _len); int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers); int tls_config_set_dheparams(struct tls_config *_config, const char *_params); int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_name); int tls_config_set_key_file(struct tls_config *_config, const char *_key_file); int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key, size_t _len); int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char *_blob_file); int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t *_blob, size_t _len); void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols); void tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth); void tls_config_prefer_ciphers_client(struct tls_config *_config); void tls_config_prefer_ciphers_server(struct tls_config *_config); void tls_config_insecure_noverifycert(struct tls_config *_config); void tls_config_insecure_noverifyname(struct tls_config *_config); void tls_config_insecure_noverifytime(struct tls_config *_config); void tls_config_verify(struct tls_config *_config); void tls_config_verify_client(struct tls_config *_config); void tls_config_verify_client_optional(struct tls_config *_config); void tls_config_clear_keys(struct tls_config *_config); int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr); struct tls *tls_client(void); struct tls *tls_server(void); int tls_configure(struct tls *_ctx, struct tls_config *_config); void tls_reset(struct tls *_ctx); void tls_free(struct tls *_ctx); int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read, int _fd_write); int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket); int tls_connect(struct tls *_ctx, const char *_host, const char *_port); int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, const char *_servername); int tls_connect_servername(struct tls *_ctx, const char *_host, const char *_port, const char *_servername); int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername); int tls_handshake(struct tls *_ctx); ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen); ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen); int tls_close(struct tls *_ctx); int tls_peer_cert_provided(struct tls *ctx); int tls_peer_cert_contains_name(struct tls *ctx, const char *name); const char * tls_peer_cert_hash(struct tls *_ctx); const char * tls_peer_cert_issuer(struct tls *ctx); const char * tls_peer_cert_subject(struct tls *ctx); time_t tls_peer_cert_notbefore(struct tls *ctx); time_t tls_peer_cert_notafter(struct tls *ctx); const char * tls_conn_version(struct tls *ctx); const char * tls_conn_cipher(struct tls *ctx); uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password); ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen); int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config); int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *client); int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text); int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size); int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size); int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size); #ifdef __cplusplus } #endif #endif /* HEADER_TLS_H */ pgqd/lib/usual/tls/tls_server.c0000664000401600040160000001115613175113172015115 0ustar cbecbe/* $OpenBSD: tls_server.c,v 1.6 2015/03/31 12:21:27 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include "tls_internal.h" struct tls * tls_server(void) { struct tls *ctx; if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_SERVER; return (ctx); } struct tls * tls_server_conn(struct tls *ctx) { struct tls *conn_ctx; if ((conn_ctx = tls_new()) == NULL) return (NULL); conn_ctx->flags |= TLS_SERVER_CONN; return (conn_ctx); } int tls_configure_server(struct tls *ctx) { EC_KEY *ecdh_key; unsigned char sid[SSL_MAX_SSL_SESSION_ID_LENGTH]; if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { tls_set_errorx(ctx, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx) != 0) goto err; if (tls_configure_keypair(ctx, 1) != 0) goto err; if (ctx->config->verify_client != 0) { int verify = SSL_VERIFY_PEER; if (ctx->config->verify_client == 1) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if (tls_configure_ssl_verify(ctx, verify) == -1) goto err; } if (ctx->config->dheparams == -1) SSL_CTX_set_dh_auto(ctx->ssl_ctx, 1); else if (ctx->config->dheparams == 1024) SSL_CTX_set_dh_auto(ctx->ssl_ctx, 2); if (ctx->config->ecdhecurve == -1) { SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1); } else if (ctx->config->ecdhecurve != NID_undef) { if ((ecdh_key = EC_KEY_new_by_curve_name( ctx->config->ecdhecurve)) == NULL) { tls_set_errorx(ctx, "failed to set ECDHE curve"); goto err; } SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh(ctx->ssl_ctx, ecdh_key); EC_KEY_free(ecdh_key); } if (ctx->config->ciphers_server == 1) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_stapling_callback) != 1) { tls_set_errorx(ctx, "ssl OCSP stapling setup failure"); goto err; } /* * Set session ID context to a random value. We don't support * persistent caching of sessions so it is OK to set a temporary * session ID context that is valid during run time. */ if (!RAND_bytes(sid, sizeof(sid))) { tls_set_errorx(ctx, "failed to generate session id"); goto err; } if (!SSL_CTX_set_session_id_context(ctx->ssl_ctx, sid, sizeof(sid))) { tls_set_errorx(ctx, "failed to set session id context"); goto err; } return (0); err: return (-1); } int tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket) { return (tls_accept_fds(ctx, cctx, socket, socket)); } int tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) { struct tls *conn_ctx = NULL; if ((ctx->flags & TLS_SERVER) == 0) { tls_set_errorx(ctx, "not a server context"); goto err; } if ((conn_ctx = tls_server_conn(ctx)) == NULL) { tls_set_errorx(ctx, "connection context failure"); goto err; } if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, "ssl failure"); goto err; } if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { tls_set_errorx(ctx, "ssl application data failure"); goto err; } if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); goto err; } *cctx = conn_ctx; return (0); err: tls_free(conn_ctx); *cctx = NULL; return (-1); } int tls_handshake_server(struct tls *ctx) { int ssl_ret; int rv = -1; if ((ctx->flags & TLS_SERVER_CONN) == 0) { tls_set_errorx(ctx, "not a server connection context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls_config.c0000664000401600040160000001713613175113172015060 0ustar cbecbe/* $OpenBSD: tls_config.c,v 1.8 2015/02/22 14:59:37 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int set_string(const char **dest, const char *src) { free((char *)*dest); *dest = NULL; if (src != NULL) if ((*dest = strdup(src)) == NULL) return -1; return 0; } static void * memdup(const void *in, size_t len) { void *out; if ((out = malloc(len)) == NULL) return NULL; memcpy(out, in, len); return out; } static int set_mem(char **dest, size_t *destlen, const void *src, size_t srclen) { free(*dest); *dest = NULL; *destlen = 0; if (src != NULL) if ((*dest = memdup(src, srclen)) == NULL) return -1; *destlen = srclen; return 0; } struct tls_config * tls_config_new(void) { struct tls_config *config; if ((config = calloc(1, sizeof(*config))) == NULL) return (NULL); /* * Default configuration. */ if (tls_config_set_ca_file(config, _PATH_SSL_CA_FILE) != 0) goto err; if (tls_config_set_dheparams(config, "none") != 0) goto err; if (tls_config_set_ecdhecurve(config, "auto") != 0) goto err; if (tls_config_set_ciphers(config, "secure") != 0) goto err; tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT); tls_config_set_verify_depth(config, 6); tls_config_prefer_ciphers_server(config); tls_config_verify(config); return (config); err: tls_config_free(config); return (NULL); } void tls_config_free(struct tls_config *config) { if (config == NULL) return; tls_config_clear_keys(config); free((char *)config->ca_file); free((char *)config->ca_path); free((char *)config->cert_file); free(config->cert_mem); free((char *)config->ciphers); free((char *)config->key_file); free(config->key_mem); free(config); } void tls_config_clear_keys(struct tls_config *config) { tls_config_set_ca_mem(config, NULL, 0); tls_config_set_cert_mem(config, NULL, 0); tls_config_set_key_mem(config, NULL, 0); } int tls_config_parse_protocols(uint32_t *protocols, const char *protostr) { uint32_t proto, protos = 0; char *s, *p, *q; int negate; if ((s = strdup(protostr)) == NULL) return (-1); q = s; while ((p = strsep(&q, ",:")) != NULL) { while (*p == ' ' || *p == '\t') p++; negate = 0; if (*p == '!') { negate = 1; p++; } if (negate && protos == 0) protos = TLS_PROTOCOLS_ALL; proto = 0; if (strcasecmp(p, "all") == 0 || strcasecmp(p, "legacy") == 0) proto = TLS_PROTOCOLS_ALL; else if (strcasecmp(p, "default") == 0 || strcasecmp(p, "secure") == 0) proto = TLS_PROTOCOLS_DEFAULT; if (strcasecmp(p, "tlsv1") == 0) proto = TLS_PROTOCOL_TLSv1; else if (strcasecmp(p, "tlsv1.0") == 0) proto = TLS_PROTOCOL_TLSv1_0; else if (strcasecmp(p, "tlsv1.1") == 0) proto = TLS_PROTOCOL_TLSv1_1; else if (strcasecmp(p, "tlsv1.2") == 0) proto = TLS_PROTOCOL_TLSv1_2; if (proto == 0) { free(s); return (-1); } if (negate) protos &= ~proto; else protos |= proto; } *protocols = protos; free(s); return (0); } int tls_config_set_ca_file(struct tls_config *config, const char *ca_file) { return set_string(&config->ca_file, ca_file); } int tls_config_set_ca_path(struct tls_config *config, const char *ca_path) { return set_string(&config->ca_path, ca_path); } int tls_config_set_ca_mem(struct tls_config *config, const uint8_t *ca, size_t len) { return set_mem(&config->ca_mem, &config->ca_len, ca, len); } int tls_config_set_cert_file(struct tls_config *config, const char *cert_file) { return set_string(&config->cert_file, cert_file); } int tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert, size_t len) { return set_mem(&config->cert_mem, &config->cert_len, cert, len); } int tls_config_set_ciphers(struct tls_config *config, const char *ciphers) { if (ciphers == NULL || strcasecmp(ciphers, "default") == 0 || strcasecmp(ciphers, "secure") == 0) ciphers = TLS_CIPHERS_DEFAULT; else if (strcasecmp(ciphers, "compat") == 0 || strcasecmp(ciphers, "legacy") == 0) ciphers = TLS_CIPHERS_COMPAT; else if (strcasecmp(ciphers, "insecure") == 0 || strcasecmp(ciphers, "all") == 0) ciphers = TLS_CIPHERS_ALL; else if (strcasecmp(ciphers, "normal") == 0) ciphers = TLS_CIPHERS_NORMAL; else if (strcasecmp(ciphers, "fast") == 0) ciphers = TLS_CIPHERS_FAST; return set_string(&config->ciphers, ciphers); } int tls_config_set_dheparams(struct tls_config *config, const char *params) { int keylen; if (params == NULL || strcasecmp(params, "none") == 0) keylen = 0; else if (strcasecmp(params, "auto") == 0) keylen = -1; else if (strcasecmp(params, "legacy") == 0) keylen = 1024; else return (-1); config->dheparams = keylen; return (0); } int tls_config_set_ecdhecurve(struct tls_config *config, const char *name) { int nid; if (name == NULL || strcasecmp(name, "none") == 0) nid = NID_undef; else if (strcasecmp(name, "auto") == 0) nid = -1; else if ((nid = OBJ_txt2nid(name)) == NID_undef) return (-1); config->ecdhecurve = nid; return (0); } int tls_config_set_key_file(struct tls_config *config, const char *key_file) { return set_string(&config->key_file, key_file); } int tls_config_set_key_mem(struct tls_config *config, const uint8_t *key, size_t len) { if (config->key_mem) explicit_bzero(config->key_mem, config->key_len); return set_mem(&config->key_mem, &config->key_len, key, len); } int tls_config_set_ocsp_stapling_file(struct tls_config *config, const char *blob_file) { if (blob_file != NULL) tls_config_set_ocsp_stapling_mem(config, NULL, 0); return set_string(&config->ocsp_file, blob_file); } int tls_config_set_ocsp_stapling_mem(struct tls_config *config, const uint8_t *blob, size_t len) { if (blob != NULL) tls_config_set_ocsp_stapling_file(config, NULL); return set_mem(&config->ocsp_mem, &config->ocsp_len, blob, len); } void tls_config_set_protocols(struct tls_config *config, uint32_t protocols) { config->protocols = protocols; } void tls_config_set_verify_depth(struct tls_config *config, int verify_depth) { config->verify_depth = verify_depth; } void tls_config_prefer_ciphers_client(struct tls_config *config) { config->ciphers_server = 0; } void tls_config_prefer_ciphers_server(struct tls_config *config) { config->ciphers_server = 1; } void tls_config_insecure_noverifycert(struct tls_config *config) { config->verify_cert = 0; } void tls_config_insecure_noverifyname(struct tls_config *config) { config->verify_name = 0; } void tls_config_insecure_noverifytime(struct tls_config *config) { config->verify_time = 0; } void tls_config_verify(struct tls_config *config) { config->verify_cert = 1; config->verify_name = 1; config->verify_time = 1; } void tls_config_verify_client(struct tls_config *config) { config->verify_client = 1; } void tls_config_verify_client_optional(struct tls_config *config) { config->verify_client = 2; } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls_internal.h0000664000401600040160000001162713175113172015433 0ustar cbecbe/* $OpenBSD: tls_internal.h,v 1.11 2015/02/22 14:50:41 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * Copyright (c) 2014 Joel Sing * * 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 HEADER_TLS_INTERNAL_H #define HEADER_TLS_INTERNAL_H #include #include #define _PATH_SSL_CA_FILE USUAL_TLS_CA_FILE /* * Anything that is not completely broken. * * Also fixes 3DES ordering bug in older OpenSSLs. */ #define TLS_CIPHERS_COMPAT "HIGH:+3DES:!aNULL" /* * All==insecure. */ #define TLS_CIPHERS_ALL "ALL:!aNULL:!eNULL" /* * TLSv1.2+AEAD+ECDHE/DHE. CBC modes are dubious due to spec bugs in TLS. */ #define TLS_CIPHERS_DEFAULT "HIGH+EECDH:HIGH+EDH:!SSLv3:!SHA384:!SHA256:!DSS:!aNULL" /* * Compact subset of reasonable suites only. * * Priorities, in order: * - ECDHE > DHE > RSA * - AESGCM > CBC * - TLSv1.2 > TLSv1.0 * - AES256 > AES128. */ #define TLS_CIPHERS_NORMAL "HIGH+EECDH:HIGH+EDH:HIGH+RSA:+SHA384:+SHA256:+SSLv3:+EDH:+RSA:-3DES:3DES+RSA:!CAMELLIA:!DSS:!aNULL" /* * Prefer performance if it does not affect security. * * Same as "normal" but prefers AES128 to AES256. */ #define TLS_CIPHERS_FAST "HIGH+EECDH:HIGH+EDH:HIGH+RSA:+AES256:+SHA256:+SHA384:+SSLv3:+EDH:+RSA:-3DES:3DES+RSA:!CAMELLIA:!DSS:!aNULL" union tls_addr { struct in_addr ip4; struct in6_addr ip6; }; struct tls_config { const char *ca_file; const char *ca_path; char *ca_mem; size_t ca_len; const char *cert_file; char *cert_mem; size_t cert_len; const char *ciphers; int ciphers_server; int dheparams; int ecdhecurve; const char *key_file; char *key_mem; size_t key_len; const char *ocsp_file; char *ocsp_mem; size_t ocsp_len; uint32_t protocols; int verify_cert; int verify_client; int verify_depth; int verify_name; int verify_time; }; struct tls_conninfo { char *issuer; char *subject; char *hash; char *serial; char *fingerprint; char *version; char *cipher; time_t notbefore; time_t notafter; }; #define TLS_CLIENT (1 << 0) #define TLS_SERVER (1 << 1) #define TLS_SERVER_CONN (1 << 2) #define TLS_OCSP_CLIENT (1 << 3) #define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0) #define TLS_HANDSHAKE_COMPLETE (1 << 1) #define TLS_DO_ABORT (1 << 8) struct tls_ocsp_query; struct tls_ocsp_info; struct tls { struct tls_config *config; uint32_t flags; uint32_t state; char *errmsg; int errnum; char *servername; int socket; SSL *ssl_conn; SSL_CTX *ssl_ctx; X509 *ssl_peer_cert; struct tls_conninfo *conninfo; int used_dh_bits; int used_ecdh_nid; const char *ocsp_result; struct tls_ocsp_info *ocsp_info; struct tls_ocsp_query *ocsp_query; }; struct tls_ocsp_info { int response_status; int cert_status; int crl_reason; time_t this_update; time_t next_update; time_t revocation_time; }; struct tls *tls_new(void); struct tls *tls_server_conn(struct tls *ctx); int tls_check_name(struct tls *ctx, X509 *cert, const char *servername); int tls_configure_keypair(struct tls *ctx, int); int tls_configure_server(struct tls *ctx); int tls_configure_ssl(struct tls *ctx); int tls_configure_ssl_verify(struct tls *ctx, int verify); int tls_handshake_client(struct tls *ctx); int tls_handshake_server(struct tls *ctx); int tls_host_port(const char *hostport, char **host, char **port); int tls_set_error(struct tls *ctx, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int tls_set_errorx(struct tls *ctx, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int tls_set_error_libssl(struct tls *ctx, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix); int tls_get_conninfo(struct tls *ctx); void tls_free_conninfo(struct tls_conninfo *conninfo); int tls_ocsp_verify_callback(SSL *ssl, void *arg); int tls_ocsp_stapling_callback(SSL *ssl, void *arg); void tls_ocsp_client_free(struct tls *ctx); void tls_ocsp_info_free(struct tls_ocsp_info *info); int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst); int asn1_time_parse(const char *, size_t, struct tm *, int); #endif /* HEADER_TLS_INTERNAL_H */ pgqd/lib/usual/tls/tls_conninfo.c0000664000401600040160000001174713175113172015426 0ustar cbecbe/* $OpenBSD: tls_peer.c,v 1.3 2015/09/11 13:22:39 beck Exp $ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int tls_hex_string(const unsigned char *in, size_t inlen, char **out, size_t *outlen) { static const char hex[] = "0123456789abcdef"; size_t i, len; char *p; if (outlen != NULL) *outlen = 0; if (inlen >= SIZE_MAX) return (-1); if ((*out = reallocarray(NULL, inlen + 1, 2)) == NULL) return (-1); p = *out; len = 0; for (i = 0; i < inlen; i++) { p[len++] = hex[(in[i] >> 4) & 0x0f]; p[len++] = hex[in[i] & 0x0f]; } p[len++] = 0; if (outlen != NULL) *outlen = len; return (0); } static int tls_get_peer_cert_hash(struct tls *ctx, char **hash) { unsigned char d[EVP_MAX_MD_SIZE]; char *dhex = NULL; unsigned int dlen; int rv = -1; *hash = NULL; if (ctx->ssl_peer_cert == NULL) return (0); if (X509_digest(ctx->ssl_peer_cert, EVP_sha256(), d, &dlen) != 1) { tls_set_errorx(ctx, "digest failed"); goto err; } if (tls_hex_string(d, dlen, &dhex, NULL) != 0) { tls_set_errorx(ctx, "digest hex string failed"); goto err; } if (asprintf(hash, "SHA256:%s", dhex) == -1) { tls_set_errorx(ctx, "out of memory"); *hash = NULL; goto err; } rv = 0; err: free(dhex); return (rv); } static int tls_get_peer_cert_issuer(struct tls *ctx, char **issuer) { X509_NAME *name = NULL; *issuer = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_issuer_name(ctx->ssl_peer_cert)) == NULL) return (-1); *issuer = X509_NAME_oneline(name, 0, 0); if (*issuer == NULL) return (-1); return (0); } static int tls_get_peer_cert_subject(struct tls *ctx, char **subject) { X509_NAME *name = NULL; *subject = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_subject_name(ctx->ssl_peer_cert)) == NULL) return (-1); *subject = X509_NAME_oneline(name, 0, 0); if (*subject == NULL) return (-1); return (0); } static int tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore, time_t *notafter) { struct tm before_tm, after_tm; ASN1_TIME *before, *after; int rv = -1; memset(&before_tm, 0, sizeof(before_tm)); memset(&after_tm, 0, sizeof(after_tm)); if (ctx->ssl_peer_cert != NULL) { if ((before = X509_get_notBefore(ctx->ssl_peer_cert)) == NULL) goto err; if ((after = X509_get_notAfter(ctx->ssl_peer_cert)) == NULL) goto err; if (asn1_time_parse((char*)before->data, before->length, &before_tm, 0) == -1) goto err; if (asn1_time_parse((char*)after->data, after->length, &after_tm, 0) == -1) goto err; if ((*notbefore = timegm(&before_tm)) == -1) goto err; if ((*notafter = timegm(&after_tm)) == -1) goto err; } rv = 0; err: return (rv); } int tls_get_conninfo(struct tls *ctx) { const char * tmp; tls_free_conninfo(ctx->conninfo); if (ctx->ssl_peer_cert != NULL) { if (tls_get_peer_cert_hash(ctx, &ctx->conninfo->hash) == -1) goto err; if (tls_get_peer_cert_subject(ctx, &ctx->conninfo->subject) == -1) goto err; if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1) goto err; if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore, &ctx->conninfo->notafter) == -1) goto err; } if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL) goto err; ctx->conninfo->version = strdup(tmp); if (ctx->conninfo->version == NULL) goto err; if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL) goto err; ctx->conninfo->cipher = strdup(tmp); if (ctx->conninfo->cipher == NULL) goto err; return (0); err: tls_free_conninfo(ctx->conninfo); return (-1); } void tls_free_conninfo(struct tls_conninfo *conninfo) { if (conninfo != NULL) { free(conninfo->hash); conninfo->hash = NULL; free(conninfo->subject); conninfo->subject = NULL; free(conninfo->issuer); conninfo->issuer = NULL; free(conninfo->version); conninfo->version = NULL; free(conninfo->cipher); conninfo->cipher = NULL; } } const char * tls_conn_cipher(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->cipher); } const char * tls_conn_version(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->version); } #endif pgqd/lib/usual/tls/tls_client.c0000664000401600040160000001536713175113172015075 0ustar cbecbe/* $OpenBSD: tls_client.c,v 1.16 2015/03/21 15:35:15 sthen Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" struct tls * tls_client(void) { struct tls *ctx; if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_CLIENT; return (ctx); } int tls_connect(struct tls *ctx, const char *host, const char *port) { return tls_connect_servername(ctx, host, port, NULL); } int tls_connect_servername(struct tls *ctx, const char *host, const char *port, const char *servername) { struct addrinfo hints, *res, *res0; const char *h = NULL, *p = NULL; char *hs = NULL, *ps = NULL; int rv = -1, s = -1, ret; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if (host == NULL) { tls_set_errorx(ctx, "host not specified"); goto err; } /* * If port is NULL try to extract a port from the specified host, * otherwise use the default. */ if ((p = (char *)port) == NULL) { ret = tls_host_port(host, &hs, &ps); if (ret == -1) { tls_set_errorx(ctx, "memory allocation failure"); goto err; } if (ret != 0) { tls_set_errorx(ctx, "no port provided"); goto err; } } h = (hs != NULL) ? hs : host; p = (ps != NULL) ? ps : port; /* * First check if the host is specified as a numeric IP address, * either IPv4 or IPv6, before trying to resolve the host. * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6 * records if it is not configured on an interface; not considering * loopback addresses. Checking the numeric addresses first makes * sure that connection attempts to numeric addresses and especially * 127.0.0.1 or ::1 loopback addresses are always possible. */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; /* try as an IPv4 literal */ hints.ai_family = AF_INET; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* try again as an IPv6 literal */ hints.ai_family = AF_INET6; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* last try, with name resolution and save the error */ hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) { tls_set_error(ctx, "%s", gai_strerror(s)); goto err; } } } /* It was resolved somehow; now try connecting to what we got */ s = -1; for (res = res0; res; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) { tls_set_error(ctx, "socket"); continue; } if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { tls_set_error(ctx, "connect"); close(s); s = -1; continue; } break; /* Connected. */ } freeaddrinfo(res0); if (s == -1) goto err; if (servername == NULL) servername = h; if (tls_connect_socket(ctx, s, servername) != 0) { close(s); goto err; } ctx->socket = s; rv = 0; err: free(hs); free(ps); return (rv); } int tls_connect_socket(struct tls *ctx, int s, const char *servername) { return tls_connect_fds(ctx, s, s, servername); } int tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, const char *servername) { union tls_addr addrbuf; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if (fd_read < 0 || fd_write < 0) { tls_set_errorx(ctx, "invalid file descriptors"); goto err; } if (servername != NULL) { if ((ctx->servername = strdup(servername)) == NULL) { tls_set_errorx(ctx, "out of memory"); goto err; } } if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { tls_set_errorx(ctx, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx) != 0) goto err; if (tls_configure_keypair(ctx, 0) != 0) goto err; if (ctx->config->verify_name) { if (servername == NULL) { tls_set_errorx(ctx, "server name not specified"); goto err; } } if (ctx->config->verify_cert && (tls_configure_ssl_verify(ctx, SSL_VERIFY_PEER) == -1)) goto err; if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_callback) != 1) { tls_set_errorx(ctx, "ssl OCSP verification setup failure"); goto err; } if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, "ssl connection failure"); goto err; } if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) { tls_set_errorx(ctx, "ssl application data failure"); goto err; } if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); goto err; } if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) { tls_set_errorx(ctx, "ssl OCSP extension setup failure"); goto err; } /* * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not * permitted in "HostName". */ if (servername != NULL && inet_pton(AF_INET, servername, &addrbuf) != 1 && inet_pton(AF_INET6, servername, &addrbuf) != 1) { if (SSL_set_tlsext_host_name(ctx->ssl_conn, servername) == 0) { tls_set_errorx(ctx, "server name indication failure"); goto err; } } rv = 0; err: return (rv); } int tls_handshake_client(struct tls *ctx) { X509 *cert = NULL; int ssl_ret; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } if (ctx->config->verify_name) { cert = SSL_get_peer_certificate(ctx->ssl_conn); if (cert == NULL) { tls_set_errorx(ctx, "no server certificate"); goto err; } if ((rv = tls_check_name(ctx, cert, ctx->servername)) != 0) { if (rv != -2) tls_set_errorx(ctx, "name `%s' not present in" " server certificate", ctx->servername); goto err; } } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: X509_free(cert); return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls_ocsp.c0000664000401600040160000005302013175113172014547 0ustar cbecbe/* * Copyright (c) 2015 Marko Kreen * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" #ifndef OPENSSL_NO_OCSP #include #define MAXAGE_SEC (14*24*60*60) #define JITTER_SEC (60) #define USE_NONCE 0 #if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10002000 #define BUGGY_VERIFY #endif /* * State for request. */ struct tls_ocsp_query { /* responder location */ char *ocsp_url; /* request blob */ uint8_t *request_data; size_t request_size; /* network state */ BIO *bio; SSL_CTX *ssl_ctx; OCSP_REQ_CTX *http_req; /* cert data, this struct does not own these */ X509 *main_cert; STACK_OF(X509) *extra_certs; SSL_CTX *cert_ssl_ctx; }; /* * Extract OCSP response info. */ static int tls_ocsp_fill_info(struct tls *ctx, int response_status, int cert_status, int crl_reason, ASN1_GENERALIZEDTIME *revtime, ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd) { struct tls_ocsp_info *info; int res; info = calloc(1, sizeof (struct tls_ocsp_info)); if (!info) { tls_set_error(ctx, "calloc"); return -1; } info->response_status = response_status; info->cert_status = cert_status; info->crl_reason = crl_reason; res = tls_asn1_parse_time(ctx, revtime, &info->revocation_time); if (res == 0) res = tls_asn1_parse_time(ctx, thisupd, &info->this_update); if (res == 0) res = tls_asn1_parse_time(ctx, nextupd, &info->next_update); if (res == 0) { ctx->ocsp_info = info; } else { tls_ocsp_info_free(info); } return res; } static void tls_ocsp_fill_result(struct tls *ctx, int res) { struct tls_ocsp_info *info = ctx->ocsp_info; if (res < 0) { ctx->ocsp_result = "error"; } else if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { ctx->ocsp_result = OCSP_response_status_str(info->response_status); } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) { ctx->ocsp_result = OCSP_cert_status_str(info->cert_status); } else { ctx->ocsp_result = OCSP_crl_reason_str(info->crl_reason); } } void tls_ocsp_info_free(struct tls_ocsp_info *info) { free(info); } int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { static const struct tls_ocsp_info no_ocsp = { -1, -1, -1, 0, 0, 0 }; const struct tls_ocsp_info *info = ctx->ocsp_info; const char *ocsp_result = ctx->ocsp_result; int ret = 0; if (!info) { info = &no_ocsp; ret = -1; } if (!ocsp_result) { ret = TLS_NO_OCSP; ocsp_result = "no-ocsp"; } if (response_status) *response_status = info->response_status; if (cert_status) *cert_status = info->cert_status; if (crl_reason) *crl_reason = info->crl_reason; if (this_update) *this_update = info->this_update; if (next_update) *next_update = info->next_update; if (revoction_time) *revoction_time = info->revocation_time; if (result_text) *result_text = ocsp_result; return ret; } /* * Verify stapled response */ static OCSP_CERTID * tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX *ssl_ctx) { X509_NAME *issuer_name; X509 *issuer; X509_STORE_CTX storectx; X509_OBJECT tmpobj; OCSP_CERTID *cid = NULL; X509_STORE *store; int ok; issuer_name = X509_get_issuer_name(main_cert); if (!issuer_name) return NULL; if (extra_certs) { issuer = X509_find_by_subject(extra_certs, issuer_name); if (issuer) return OCSP_cert_to_id(NULL, main_cert, issuer); } store = SSL_CTX_get_cert_store(ssl_ctx); if (!store) return NULL; ok = X509_STORE_CTX_init(&storectx, store, main_cert, extra_certs); if (ok != 1) return NULL; ok = X509_STORE_get_by_subject(&storectx, X509_LU_X509, issuer_name, &tmpobj); if (ok == 1) { cid = OCSP_cert_to_id(NULL, main_cert, tmpobj.data.x509); X509_free(tmpobj.data.x509); } X509_STORE_CTX_cleanup(&storectx); return cid; } static int tls_ocsp_verify_response(struct tls *ctx, X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX *ssl_ctx, OCSP_RESPONSE *resp) { OCSP_BASICRESP *br = NULL; STACK_OF(X509) *ocsp_chain = NULL; ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; OCSP_CERTID *cid = NULL; STACK_OF(X509) *combined = NULL; int response_status=0, cert_status=0, crl_reason=0; int ssl_res, ret = -1; unsigned long flags; #ifdef BUGGY_VERIFY STACK_OF(X509) *tmpchain = NULL; #endif br = OCSP_response_get1_basic(resp); if (!br) { tls_set_errorx(ctx, "ocsp error: cannot load"); goto error; } #ifdef BUGGY_VERIFY /* * There may be OCSP-subCA in OCSP response that chains to subCA * in main TLS headers. Need to present both chains to verify. * * OCSP_basic_verify() bugs: * - Does not use br->certs when building chain. * - Does not use main_certs when validating br->certs. */ if (br->certs && extra_certs) { int i; combined = sk_X509_new_null(); if (!combined) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } for (i = 0; i < sk_X509_num(br->certs); i++) { ssl_res = sk_X509_push(combined, sk_X509_value(br->certs, i)); if (ssl_res == 0) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } } for (i = 0; i < sk_X509_num(extra_certs); i++) { ssl_res = sk_X509_push(combined, sk_X509_value(extra_certs, i)); if (ssl_res == 0) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } } /* OCSP_TRUSTOTHER would skip br->certs checks */ flags = 0; flags = OCSP_TRUSTOTHER; ocsp_chain = combined; /* Bug: does not use main_certs when validating br->certs */ if ((flags & OCSP_TRUSTOTHER) == 0) { tmpchain = br->certs; br->certs = combined; } } else if (br->certs && !extra_certs) { /* can this happen? */ flags = 0; ocsp_chain = br->certs; } else #endif { /* * Skip validation of 'extra_certs' as this should be done * already as part of main handshake. */ flags = OCSP_TRUSTOTHER; ocsp_chain = extra_certs; } /* now verify */ ssl_res = OCSP_basic_verify(br, ocsp_chain, SSL_CTX_get_cert_store(ssl_ctx), flags); #ifdef BUGGY_VERIFY /* replace back */ if (tmpchain) { br->certs = tmpchain; tmpchain = NULL; } #endif if (ssl_res != 1) { tls_set_error_libssl(ctx, "ocsp verify failed"); goto error; } /* signature OK, look inside */ response_status = OCSP_response_status(resp); if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { tls_set_errorx(ctx, "ocsp verify failed: unsuccessful response - %s", OCSP_response_status_str(response_status)); goto error; } cid = tls_ocsp_get_certid(main_cert, extra_certs, ssl_ctx); if (!cid) { tls_set_errorx(ctx, "ocsp verify failed: no issuer cert"); goto error; } ssl_res = OCSP_resp_find_status(br, cid, &cert_status, &crl_reason, &revtime, &thisupd, &nextupd); if (ssl_res != 1) { tls_set_errorx(ctx, "ocsp verify failed: no result for cert"); goto error; } ssl_res = OCSP_check_validity(thisupd, nextupd, JITTER_SEC, MAXAGE_SEC); if (ssl_res != 1) { tls_set_errorx(ctx, "ocsp verify failed: bad age"); goto error; } ssl_res = tls_ocsp_fill_info(ctx, response_status, cert_status, crl_reason, revtime, thisupd, nextupd); if (ssl_res != 0) goto error; /* finally can look at status */ if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status != V_OCSP_CERTSTATUS_UNKNOWN) { tls_set_errorx(ctx, "ocsp verify failed: revoked cert - %s", OCSP_crl_reason_str(crl_reason)); goto error; } ret = 0; error: sk_X509_free(combined); OCSP_CERTID_free(cid); OCSP_BASICRESP_free(br); return ret; } /* * Same callback on client-side has different error proto: * 1=OK, 0=bad, -1=internal error */ int tls_ocsp_verify_callback(SSL *ssl, void *arg) { OCSP_RESPONSE *resp = NULL; STACK_OF(X509) *extra_certs = NULL; X509 *peer = NULL; const unsigned char *raw = NULL; int size, res = -1; struct tls *ctx; ctx = SSL_get_app_data(ssl); if (!ctx) return -1; size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw); if (size <= 0) return 1; peer = SSL_get_peer_certificate(ssl); if (!peer) { tls_set_errorx(ctx, "ocsp verify failed: no peer cert"); goto error; } resp = d2i_OCSP_RESPONSE(NULL, &raw, size); if (!resp) { tls_set_errorx(ctx, "ocsp verify failed: parse failed"); goto error; } extra_certs = SSL_get_peer_cert_chain(ssl); res = tls_ocsp_verify_response(ctx, peer, extra_certs, ctx->ssl_ctx, resp); error: tls_ocsp_fill_result(ctx, res); OCSP_RESPONSE_free(resp); X509_free(peer); return (res == 0) ? 1 : 0; } /* * Staple OCSP response to server handshake. */ int tls_ocsp_stapling_callback(SSL *ssl, void *arg) { struct tls *ctx; char *mem, *fmem = NULL; uint8_t *xmem; size_t len; int ret = SSL_TLSEXT_ERR_ALERT_FATAL; ctx = SSL_get_app_data(ssl); if (!ctx) return SSL_TLSEXT_ERR_NOACK; if (ctx->config->ocsp_file) { fmem = mem = (char*)tls_load_file(ctx->config->ocsp_file, &len, NULL); if (!mem) goto err; } else { mem = ctx->config->ocsp_mem; len = ctx->config->ocsp_len; if (!mem) return SSL_TLSEXT_ERR_NOACK; } xmem = OPENSSL_malloc(len); if (xmem) { memcpy(xmem, mem, len); if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, xmem, len) != 1) { OPENSSL_free(xmem); goto err; } ret = SSL_TLSEXT_ERR_OK; } err: free(fmem); return ret; } /* * Query OCSP responder over HTTP(S). */ void tls_ocsp_client_free(struct tls *ctx) { struct tls_ocsp_query *q; if (!ctx) return; q = ctx->ocsp_query; if (q) { if (q->http_req) OCSP_REQ_CTX_free(q->http_req); BIO_free_all(q->bio); SSL_CTX_free(q->ssl_ctx); free(q->ocsp_url); free(q->request_data); free(q); ctx->ocsp_query = NULL; } } static struct tls * tls_ocsp_client_new(void) { struct tls *ctx; ctx = tls_new(); if (!ctx) return NULL; ctx->flags = TLS_OCSP_CLIENT; ctx->ocsp_query = calloc(1, sizeof (struct tls_ocsp_query)); if (!ctx->ocsp_query) { tls_free(ctx); return NULL; } return ctx; } static int tls_build_ocsp_request(struct tls *ctx) { struct tls_ocsp_query *q; int ok, ret = -1; OCSP_REQUEST *req = NULL; OCSP_CERTID *cid = NULL; OCSP_ONEREQ *onereq = NULL; BIO *mem = NULL; void *data; q = ctx->ocsp_query; cid = tls_ocsp_get_certid(q->main_cert, q->extra_certs, q->cert_ssl_ctx); if (!cid) { tls_set_errorx(ctx, "Cannot create cert-id"); goto failed; } req = OCSP_REQUEST_new(); if (!req) { tls_set_error_libssl(ctx, "Cannot create request"); goto failed; } onereq = OCSP_request_add0_id(req, cid); if (!onereq) { tls_set_error_libssl(ctx, "Cannot add cert-id to request"); goto failed; } cid = NULL; /* * Now render it. */ mem = BIO_new(BIO_s_mem()); if (!mem) { tls_set_errorx(ctx, "BIO_new"); goto failed; } ok = i2d_OCSP_REQUEST_bio(mem, req); if (!ok) { tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio"); goto failed; } q->request_size = BIO_get_mem_data(mem, &data); q->request_data = malloc(q->request_size); if (!q->request_data) { tls_set_error(ctx, "Failed to allocate request data"); goto failed; } memcpy(q->request_data, data, q->request_size); req = NULL; ret = 0; failed: OCSP_CERTID_free(cid); OCSP_REQUEST_free(req); BIO_free(mem); return ret; } static int tls_ocsp_setup(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls *target) { struct tls *ctx; struct tls_ocsp_query *q; int ret; STACK_OF(OPENSSL_STRING) *ocsp_urls; ctx = tls_ocsp_client_new(); if (!ctx) return -1; *ocsp_ctx_p = ctx; q = ctx->ocsp_query; if (config) { /* create ctx->ssl_ctx */ ctx->flags = TLS_SERVER; ret = tls_configure(ctx, config); ctx->flags = TLS_OCSP_CLIENT; if (ret != 0) return ret; q->main_cert = SSL_get_certificate(ctx->ssl_conn); q->cert_ssl_ctx = ctx->ssl_ctx; SSL_CTX_get_extra_chain_certs(ctx->ssl_ctx, &q->extra_certs); } else { /* steal state from target struct */ q->main_cert = SSL_get_peer_certificate(target->ssl_conn); q->extra_certs = SSL_get_peer_cert_chain(target->ssl_conn); q->cert_ssl_ctx = target->ssl_ctx; X509_free(q->main_cert); /* unref */ } if (!q->main_cert) { tls_set_errorx(ctx, "No cert"); return -1; } ocsp_urls = X509_get1_ocsp(q->main_cert); if (!ocsp_urls) return TLS_NO_OCSP; q->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0)); if (!q->ocsp_url) { tls_set_errorx(ctx, "Cannot copy URL"); goto failed; } ret = tls_build_ocsp_request(ctx); if (ret != 0) goto failed; *ocsp_ctx_p = ctx; failed: X509_email_free(ocsp_urls); return ret; } static int tls_ocsp_process_response_parsed(struct tls *ctx, struct tls_config *config, OCSP_RESPONSE *resp) { struct tls_ocsp_query *q = ctx->ocsp_query; BIO *mem = NULL; size_t len; unsigned char *data; int ret = -1, ok, res; res = tls_ocsp_verify_response(ctx, q->main_cert, q->extra_certs, q->cert_ssl_ctx, resp); if (res < 0) goto failed; /* Update blob in config */ if (config) { mem = BIO_new(BIO_s_mem()); if (!mem) { tls_set_error_libssl(ctx, "BIO_new"); goto failed; } ok = i2d_OCSP_RESPONSE_bio(mem, resp); if (!ok) { tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio"); goto failed; } len = BIO_get_mem_data(mem, &data); res = tls_config_set_ocsp_stapling_mem(config, data, len); if (res < 0) goto failed; } ret = 0; failed: BIO_free(mem); tls_ocsp_fill_result(ctx, ret); return ret; } static int tls_ocsp_create_request(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { int res; struct tls_ocsp_query *q; res = tls_ocsp_setup(ocsp_ctx_p, config, target); if (res != 0) return res; q = (*ocsp_ctx_p)->ocsp_query; *ocsp_url = q->ocsp_url; *request_blob = q->request_data; *request_size = q->request_size; return 0; } /* * Public API for request blobs. */ int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { return tls_ocsp_create_request(ocsp_ctx_p, NULL, target, ocsp_url, request_blob, request_size); } int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size) { return tls_ocsp_create_request(ocsp_ctx_p, config, NULL, ocsp_url, request_blob, request_size); } int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size) { int ret; OCSP_RESPONSE *resp; const unsigned char *raw = response_blob; resp = d2i_OCSP_RESPONSE(NULL, &raw, size); if (!resp) { ctx->ocsp_result = "parse-failed"; tls_set_error_libssl(ctx, "parse failed"); return -1; } ret = tls_ocsp_process_response_parsed(ctx, ctx->config, resp); OCSP_RESPONSE_free(resp); return ret; } /* * Network processing */ static int tls_ocsp_build_http_req(struct tls *ctx) { struct tls_ocsp_query *q = ctx->ocsp_query; int ok; OCSP_REQUEST *req; OCSP_REQ_CTX *sreq; const unsigned char *data; int ret=-1, https=0; char *host=NULL, *port=NULL, *path=NULL; ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https); if (ok != 1) { tls_set_error_libssl(ctx, "Cannot parse URL"); goto failed; } sreq = OCSP_sendreq_new(q->bio, path, NULL, -1); if (!sreq) { tls_set_error_libssl(ctx, "OCSP HTTP request setup failed"); goto failed; } q->http_req = sreq; ok = OCSP_REQ_CTX_add1_header(sreq, "Host", host); /* add payload after headers */ if (ok) { data = q->request_data; req = d2i_OCSP_REQUEST(NULL, &data, q->request_size); if (req) ok = OCSP_REQ_CTX_set1_req(sreq, req); else ok = false; OCSP_REQUEST_free(req); } if (ok) ret = 0; failed: free(host); free(port); free(path); return ret; } static int tls_ocsp_connection_setup(struct tls *ctx) { SSL *ssl = NULL; int ret = -1, ok, https=0; struct tls_ocsp_query *q = ctx->ocsp_query; union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; char *host=NULL, *port=NULL, *path=NULL; ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https); if (ok != 1) { tls_set_error_libssl(ctx, "Cannot parse URL"); goto failed; } if (https) { q->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (!q->ssl_ctx) { tls_set_error_libssl(ctx, "Cannot init SSL"); goto failed; } SSL_CTX_set_options(q->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); SSL_CTX_clear_options(q->ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); SSL_CTX_set_cipher_list(q->ssl_ctx, TLS_CIPHERS_COMPAT); SSL_CTX_set_mode(q->ssl_ctx, SSL_MODE_AUTO_RETRY); q->bio = BIO_new_ssl_connect(q->ssl_ctx); if (q->bio) { if (inet_pton(AF_INET, host, &addrbuf) != 1 && inet_pton(AF_INET6, host, &addrbuf) != 1) { if (!BIO_get_ssl(q->bio, &ssl)) { tls_set_errorx(ctx, "cannot get ssl struct"); goto failed; } if (SSL_set_tlsext_host_name(ssl, host) == 0) { tls_set_errorx(ctx, "server name indication failure"); goto failed; } } } } else { q->bio = BIO_new(BIO_s_connect()); } if (!q->bio) { tls_set_error_libssl(ctx, "Cannot connect"); goto failed; } BIO_set_conn_hostname(q->bio, host); BIO_set_conn_port(q->bio, port); BIO_set_nbio(q->bio, 1); ret = 0; failed: free(host); free(port); free(path); return ret; } static int tls_ocsp_evloop(struct tls *ctx, int *fd_p, struct tls_config *config) { struct tls_ocsp_query *q = ctx->ocsp_query; OCSP_RESPONSE *ocsp_resp = NULL; int ret, ok; if (q->http_req == NULL) { ok = BIO_do_connect(q->bio); if (ok != 1 && !BIO_should_retry(q->bio)) { tls_set_error_libssl(ctx, "Connection failure"); goto error; } *fd_p = BIO_get_fd(q->bio, NULL); if (*fd_p < 0) { tls_set_error_libssl(ctx, "Cannot get FD"); goto error; } if (ok != 1) return TLS_WANT_POLLOUT; ret = tls_ocsp_build_http_req(ctx); if (ret != 0) goto error; } ok = OCSP_sendreq_nbio(&ocsp_resp, q->http_req); if (ok == 1) { ret = tls_ocsp_process_response_parsed(ctx, config, ocsp_resp); return ret; } else if (ok == 0) { tls_set_error_libssl(ctx, "OCSP request failed"); goto error; } else if (BIO_should_read(q->bio)) { return TLS_WANT_POLLIN; } else if (BIO_should_write(q->bio)) { return TLS_WANT_POLLOUT; } tls_set_error_libssl(ctx, "Unexpected request error"); error: tls_ocsp_fill_result(ctx, -1); return -1; } static int tls_ocsp_do_poll(struct tls *ctx, int errcode, int fd) { struct pollfd pfd; int res; memset(&pfd, 0, sizeof(pfd)); pfd.fd = fd; if (errcode == TLS_WANT_POLLIN) { pfd.events = POLLIN; } else if (errcode == TLS_WANT_POLLOUT) { pfd.events = POLLOUT; } else { tls_set_error(ctx, "bad code to poll"); return -1; } res = poll(&pfd, 1, -1); if (res == 1) { return 0; } else if (res == 0) { tls_set_errorx(ctx, "poll timed out"); return -1; } tls_set_error(ctx, "poll error"); return -1; } static int tls_ocsp_query_async(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target) { struct tls *ctx = *ocsp_ctx_p; int ret; if (!ctx) { ret = tls_ocsp_setup(&ctx, config, target); if (ret != 0) goto failed; ret = tls_ocsp_connection_setup(ctx); if (ret != 0) goto failed; *ocsp_ctx_p = ctx; } return tls_ocsp_evloop(ctx, fd_p, config); failed: tls_free(ctx); return -1; } static int tls_ocsp_common_query(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target) { struct tls *ctx = NULL; int ret, fd; /* async path */ if (fd_p) return tls_ocsp_query_async(ocsp_ctx_p, fd_p, config, target); /* sync path */ while (1) { ret = tls_ocsp_query_async(&ctx, &fd, config, target); if (ret != TLS_WANT_POLLIN && ret != TLS_WANT_POLLOUT) break; ret = tls_ocsp_do_poll(ctx, ret, fd); if (ret != 0) break; } *ocsp_ctx_p = ctx; return ret; } /* * Public API. */ int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target) { return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, NULL, target); } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, config, NULL); } #else /* No OCSP */ void tls_ocsp_info_free(struct tls_ocsp_info *info) {} void tls_ocsp_client_free(struct tls *ctx) {} int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { if (response_status) *response_status = -1; if (cert_status) *cert_status = -1; if (crl_reason) *crl_reason = -1; if (result_text) *result_text = "OCSP not supported"; if (this_update) *this_update = 0; if (next_update) *next_update = 0; if (revoction_time) *revoction_time = 0; return TLS_NO_OCSP; } int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target) { *ocsp_ctx_p = NULL; return TLS_NO_OCSP; } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { *ocsp_ctx_p = NULL; return TLS_NO_OCSP; } #endif /* OPENSSL_NO_OCSP */ #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls.c0000664000401600040160000003244713175113172013535 0ustar cbecbe/* $OpenBSD: tls.c,v 1.11 2015/04/15 16:08:43 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include #include #include #include "tls_internal.h" static struct tls_config *tls_config_default; static int tls_initialised = 0; int tls_init(void) { if (tls_initialised) return (0); SSL_load_error_strings(); SSL_library_init(); if (BIO_sock_init() != 1) return (-1); if ((tls_config_default = tls_config_new()) == NULL) return (-1); tls_initialised = 1; return (0); } void tls_deinit(void) { if (tls_initialised) { tls_compat_cleanup(); tls_config_free(tls_config_default); tls_config_default = NULL; EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); BIO_sock_cleanup(); ERR_clear_error(); ERR_remove_thread_state(NULL); ERR_free_strings(); tls_initialised = 0; } } const char * tls_error(struct tls *ctx) { return ctx->errmsg; } _PRINTF(3,0) static int tls_set_verror(struct tls *ctx, int errnum, const char *fmt, va_list ap) { char *errmsg = NULL; int rv = -1; free(ctx->errmsg); ctx->errmsg = NULL; if (vasprintf(&errmsg, fmt, ap) == -1) { errmsg = NULL; goto err; } if (errnum == -1) { ctx->errmsg = errmsg; return (0); } if (asprintf(&ctx->errmsg, "%s: %s", errmsg, strerror(errnum)) == -1) { ctx->errmsg = NULL; goto err; } rv = 0; err: free(errmsg); return (rv); } int tls_set_error(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; ctx->errnum = errno; va_start(ap, fmt); rv = tls_set_verror(ctx, ctx->errnum, fmt, ap); va_end(ap); return (rv); } int tls_set_errorx(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_set_verror(ctx, -1, fmt, ap); va_end(ap); return (rv); } int tls_set_error_libssl(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; const char *msg = NULL; char *old; int err; err = ERR_peek_error(); if (err != 0) msg = ERR_reason_error_string(err); va_start(ap, fmt); rv = tls_set_verror(ctx, -1, fmt, ap); va_end(ap); if (rv != 0 || msg == NULL) return rv; old = ctx->errmsg; ctx->errmsg = NULL; if (asprintf(&ctx->errmsg, "%s: %s", old, msg) == -1) { ctx->errmsg = old; return 0; } free(old); return 0; } struct tls * tls_new(void) { struct tls *ctx; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) return (NULL); ctx->config = tls_config_default; tls_reset(ctx); return (ctx); } int tls_configure(struct tls *ctx, struct tls_config *config) { if (config == NULL) config = tls_config_default; ctx->config = config; if ((ctx->flags & TLS_SERVER) != 0) return (tls_configure_server(ctx)); return (0); } int tls_configure_keypair(struct tls *ctx, int required) { EVP_PKEY *pkey = NULL; X509 *cert = NULL; BIO *bio = NULL; if (!required && ctx->config->cert_mem == NULL && ctx->config->key_mem == NULL && ctx->config->cert_file == NULL && ctx->config->key_file == NULL) return(0); if (ctx->config->cert_mem != NULL) { if (ctx->config->cert_len > INT_MAX) { tls_set_errorx(ctx, "certificate too long"); goto err; } if (SSL_CTX_use_certificate_chain_mem(ctx->ssl_ctx, ctx->config->cert_mem, ctx->config->cert_len) != 1) { tls_set_errorx(ctx, "failed to load certificate"); goto err; } cert = NULL; } if (ctx->config->key_mem != NULL) { if (ctx->config->key_len > INT_MAX) { tls_set_errorx(ctx, "key too long"); goto err; } if ((bio = BIO_new_mem_buf(ctx->config->key_mem, ctx->config->key_len)) == NULL) { tls_set_errorx(ctx, "failed to create buffer"); goto err; } if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL)) == NULL) { tls_set_errorx(ctx, "failed to read private key"); goto err; } if (SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey) != 1) { tls_set_errorx(ctx, "failed to load private key"); goto err; } BIO_free(bio); bio = NULL; EVP_PKEY_free(pkey); pkey = NULL; } if (ctx->config->cert_file != NULL) { if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, ctx->config->cert_file) != 1) { tls_set_errorx(ctx, "failed to load certificate file"); goto err; } } if (ctx->config->key_file != NULL) { if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, ctx->config->key_file, SSL_FILETYPE_PEM) != 1) { tls_set_errorx(ctx, "failed to load private key file"); goto err; } } if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) { tls_set_errorx(ctx, "private/public key mismatch"); goto err; } return (0); err: EVP_PKEY_free(pkey); X509_free(cert); BIO_free(bio); return (1); } static void tls_info_callback(const SSL *ssl, int where, int rc) { struct tls *ctx = SSL_get_app_data(ssl); #ifdef USE_LIBSSL_INTERNALS if (!(ctx->state & TLS_HANDSHAKE_COMPLETE) && ssl->s3) { /* steal info about used DH key */ if (ssl->s3->tmp.dh && !ctx->used_dh_bits) { ctx->used_dh_bits = DH_size(ssl->s3->tmp.dh) * 8; } else if (ssl->s3->tmp.ecdh && !ctx->used_ecdh_nid) { ctx->used_ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ssl->s3->tmp.ecdh)); } } #endif /* detect renegotation on established connection */ if (where & SSL_CB_HANDSHAKE_START) { if (ctx->state & TLS_HANDSHAKE_COMPLETE) ctx->state |= TLS_DO_ABORT; } } static int tls_do_abort(struct tls *ctx) { int ssl_ret, rv; ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) return (rv); } tls_set_errorx(ctx, "unexpected handshake, closing connection"); return -1; } int tls_configure_ssl(struct tls *ctx) { SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); if (ctx->config->ciphers != NULL) { if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, ctx->config->ciphers) != 1) { tls_set_errorx(ctx, "failed to set ciphers"); goto err; } } SSL_CTX_set_info_callback(ctx->ssl_ctx, tls_info_callback); #ifdef X509_V_FLAG_NO_CHECK_TIME if (ctx->config->verify_time == 0) { X509_VERIFY_PARAM *vfp = SSL_CTX_get0_param(ctx->ssl_ctx); X509_VERIFY_PARAM_set_flags(vfp, X509_V_FLAG_NO_CHECK_TIME); } #endif return (0); err: return (-1); } int tls_configure_ssl_verify(struct tls *ctx, int verify) { SSL_CTX_set_verify(ctx->ssl_ctx, verify, NULL); if (ctx->config->ca_mem != NULL) { /* XXX do this in set. */ if (ctx->config->ca_len > INT_MAX) { tls_set_errorx(ctx, "ca too long"); goto err; } if (SSL_CTX_load_verify_mem(ctx->ssl_ctx, ctx->config->ca_mem, ctx->config->ca_len) != 1) { tls_set_errorx(ctx, "ssl verify memory setup failure"); goto err; } } else if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ctx->config->ca_file, ctx->config->ca_path) != 1) { tls_set_errorx(ctx, "ssl verify setup failure"); goto err; } if (ctx->config->verify_depth >= 0) SSL_CTX_set_verify_depth(ctx->ssl_ctx, ctx->config->verify_depth); return (0); err: return (-1); } void tls_free(struct tls *ctx) { if (ctx == NULL) return; tls_reset(ctx); free(ctx); } void tls_reset(struct tls *ctx) { SSL_CTX_free(ctx->ssl_ctx); SSL_free(ctx->ssl_conn); X509_free(ctx->ssl_peer_cert); ctx->ssl_conn = NULL; ctx->ssl_ctx = NULL; ctx->ssl_peer_cert = NULL; ctx->socket = -1; ctx->state = 0; free(ctx->servername); ctx->servername = NULL; free(ctx->errmsg); ctx->errmsg = NULL; ctx->errnum = 0; tls_free_conninfo(ctx->conninfo); free(ctx->conninfo); ctx->conninfo = NULL; ctx->used_dh_bits = 0; ctx->used_ecdh_nid = 0; tls_ocsp_info_free(ctx->ocsp_info); ctx->ocsp_info = NULL; ctx->ocsp_result = NULL; if (ctx->flags & TLS_OCSP_CLIENT) tls_ocsp_client_free(ctx); } int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix) { const char *errstr = "unknown error"; unsigned long err; int ssl_err; ssl_err = SSL_get_error(ssl_conn, ssl_ret); switch (ssl_err) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: return (0); case SSL_ERROR_WANT_READ: return (TLS_WANT_POLLIN); case SSL_ERROR_WANT_WRITE: return (TLS_WANT_POLLOUT); case SSL_ERROR_SYSCALL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } else if (ssl_ret == 0) { if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { errstr = "Unexpected EOF"; } else { ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY; return (0); } } else if (ssl_ret == -1) { errstr = strerror(errno); } tls_set_errorx(ctx, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_SSL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } tls_set_errorx(ctx, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: default: tls_set_errorx(ctx, "%s failed (%i)", prefix, ssl_err); return (-1); } } int tls_handshake(struct tls *ctx) { int rv = -1; if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); goto out; } if (ctx->conninfo == NULL && (ctx->conninfo = calloc(1, sizeof(*ctx->conninfo))) == NULL) goto out; if ((ctx->flags & TLS_CLIENT) != 0) rv = tls_handshake_client(ctx); else if ((ctx->flags & TLS_SERVER_CONN) != 0) rv = tls_handshake_server(ctx); if (rv == 0) { ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn); if (tls_get_conninfo(ctx) == -1) rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_read(struct tls *ctx, void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; if (ctx->state & TLS_DO_ABORT) { rv = tls_do_abort(ctx); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "read"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_write(struct tls *ctx, const void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; if (ctx->state & TLS_DO_ABORT) { rv = tls_do_abort(ctx); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } int tls_close(struct tls *ctx) { int ssl_ret; int rv = 0; if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); rv = -1; goto out; } if (ctx->ssl_conn != NULL) { ERR_clear_error(); ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) goto out; } } if (ctx->socket != -1) { if (shutdown(ctx->socket, SHUT_RDWR) != 0) { if (rv == 0 && errno != ENOTCONN && errno != ECONNRESET) { tls_set_error(ctx, "shutdown"); rv = -1; } } if (close(ctx->socket) != 0) { if (rv == 0) { tls_set_error(ctx, "close"); rv = -1; } } ctx->socket = -1; } if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) { tls_set_errorx(ctx, "EOF without close notify"); rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls_util.c0000664000401600040160000001214513175113172014563 0ustar cbecbe/* $OpenBSD: tls_util.c,v 1.1 2014/10/31 13:46:17 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * Copyright (c) 2015 Reyk Floeter * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" const char * tls_backend_version(void) { return SSLeay_version(SSLEAY_VERSION); } /* * Extract the host and port from a colon separated value. For a literal IPv6 * address the address must be contained with square braces. If a host and * port are successfully extracted, the function will return 0 and the * caller is responsible for freeing the host and port. If no port is found * then the function will return 1, with both host and port being NULL. * On memory allocation failure -1 will be returned. */ int tls_host_port(const char *hostport, char **host, char **port) { char *h, *p, *s; int rv = 1; *host = NULL; *port = NULL; if ((s = strdup(hostport)) == NULL) goto fail; h = p = s; /* See if this is an IPv6 literal with square braces. */ if (p[0] == '[') { h++; if ((p = strchr(s, ']')) == NULL) goto done; *p++ = '\0'; } /* Find the port seperator. */ if ((p = strchr(p, ':')) == NULL) goto done; /* If there is another separator then we have issues. */ if (strchr(p + 1, ':') != NULL) goto done; *p++ = '\0'; if (asprintf(host, "%s", h) == -1) goto fail; if (asprintf(port, "%s", p) == -1) goto fail; rv = 0; goto done; fail: free(*host); *host = NULL; free(*port); *port = NULL; rv = -1; done: free(s); return (rv); } static int tls_password_cb(char *buf, int size, int rwflag, void *u) { size_t len; if (u == NULL) { memset(buf, 0, size); return (0); } if ((len = strlcpy(buf, u, size)) >= (size_t)size) return (0); return (len); } uint8_t * tls_load_file(const char *name, size_t *len, char *password) { FILE *fp; EVP_PKEY *key = NULL; BIO *bio = NULL; uint8_t *buf = NULL; char *data; struct stat st; size_t size; int fd = -1; *len = 0; if ((fd = open(name, O_RDONLY)) == -1) return (NULL); /* Just load the file into memory without decryption */ if (password == NULL) { if (fstat(fd, &st) != 0) goto fail; size = (size_t)st.st_size; if ((buf = calloc(1, size + 1)) == NULL) goto fail; if (read(fd, buf, size) != (ssize_t)size) goto fail; close(fd); goto done; } /* Or read the (possibly) encrypted key from file */ if ((fp = fdopen(fd, "r")) == NULL) goto fail; fd = -1; key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password); fclose(fp); if (key == NULL) goto fail; /* Write unencrypted key to memory buffer */ if ((bio = BIO_new(BIO_s_mem())) == NULL) goto fail; if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) goto fail; if ((size = BIO_get_mem_data(bio, &data)) <= 0) goto fail; if ((buf = calloc(1, size)) == NULL) goto fail; memcpy(buf, data, size); BIO_free_all(bio); EVP_PKEY_free(key); done: *len = size; return (buf); fail: free(buf); if (fd != -1) close(fd); if (bio != NULL) BIO_free_all(bio); if (key != NULL) EVP_PKEY_free(key); return (NULL); } ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen) { SSL *conn = ctx->ssl_conn; const char *ocsp_pfx = "", *ocsp_info = ""; const char *proto = "-", *cipher = "-"; char dh[64]; int used_dh_bits = ctx->used_dh_bits, used_ecdh_nid = ctx->used_ecdh_nid; if (conn != NULL) { proto = SSL_get_version(conn); cipher = SSL_get_cipher(conn); #ifdef SSL_get_server_tmp_key if (ctx->flags & TLS_CLIENT) { EVP_PKEY *pk = NULL; int ok = SSL_get_server_tmp_key(conn, &pk); if (ok) { int pk_type = EVP_PKEY_id(pk); if (pk_type == EVP_PKEY_DH) { DH *dh = EVP_PKEY_get0(pk); used_dh_bits = DH_size(dh) * 8; } else if (pk_type == EVP_PKEY_EC) { EC_KEY *ecdh = EVP_PKEY_get0(pk); const EC_GROUP *eg = EC_KEY_get0_group(ecdh); used_ecdh_nid = EC_GROUP_get_curve_name(eg); } EVP_PKEY_free(pk); } } #endif } if (used_dh_bits) { snprintf(dh, sizeof dh, "/DH=%d", used_dh_bits); } else if (used_ecdh_nid) { snprintf(dh, sizeof dh, "/ECDH=%s", OBJ_nid2sn(used_ecdh_nid)); } else { dh[0] = 0; } if (ctx->ocsp_result) { ocsp_info = ctx->ocsp_result; ocsp_pfx = "/OCSP="; } return snprintf(buf, buflen, "%s/%s%s%s%s", proto, cipher, dh, ocsp_pfx, ocsp_info); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls_cert.c0000664000401600040160000004102713175113172014544 0ustar cbecbe/* $OpenBSD: tls_verify.c,v 1.7 2015/02/11 06:46:33 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" #define TLS_CERT_INTERNAL_FUNCS #include "tls_cert.h" /* * Load cert data from X509 cert. */ /* Upper bounds */ #define UB_COMMON_NAME 255 #define UB_COUNTRY_NAME 255 #define UB_STATE_NAME 255 #define UB_LOCALITY_NAME 255 #define UB_STREET_ADDRESS 255 #define UB_ORGANIZATION_NAME 255 #define UB_ORGANIZATIONAL_UNIT_NAME 255 #define UB_GNAME_DNS 255 #define UB_GNAME_EMAIL 255 #define UB_GNAME_URI 255 /* Convert ASN1_INTEGER to decimal string string */ static int tls_parse_bigint(struct tls *ctx, const ASN1_INTEGER *asn1int, const char **dst_p) { long small; BIGNUM *big; char *tmp, buf[64]; *dst_p = NULL; small = ASN1_INTEGER_get(asn1int); if (small < 0) { big = ASN1_INTEGER_to_BN(asn1int, NULL); if (big) { tmp = BN_bn2dec(big); if (tmp) *dst_p = strdup(tmp); OPENSSL_free(tmp); } BN_free(big); } else { snprintf(buf, sizeof buf, "%lu", small); *dst_p = strdup(buf); } if (*dst_p) return 0; tls_set_errorx(ctx, "cannot parse serial"); return -1; } /* * Decode all string types used in RFC5280. * * OpenSSL used (before Jun 1 2014 commit) to pick between PrintableString, * T61String, BMPString and UTF8String, depending on data. This code * tries to match that. * * Disallow any ancient ASN.1 escape sequences. */ static int check_invalid_bytes(struct tls *ctx, unsigned char *data, unsigned int len, int ascii_only, const char *desc) { unsigned int i, c; /* data is utf8 string, check for crap */ for (i = 0; i < len; i++) { c = data[i]; if (ascii_only && (c & 0x80) != 0) { tls_set_errorx(ctx, "invalid %s: contains non-ascii in ascii string", desc); goto failed; } else if (c < 0x20) { /* ascii control chars, including NUL */ if (c != '\t' && c != '\n' && c != '\r') { tls_set_errorx(ctx, "invalid %s: contains C0 control char", desc); goto failed; } } else if (c == 0xC2 && (i + 1) < len) { /* C1 control chars in UTF-8: \xc2\x80 - \xc2\x9f */ c = data[i + 1]; if (c >= 0x80 && c <= 0x9F) { tls_set_errorx(ctx, "invalid %s: contains C1 control char", desc); goto failed; } } else if (c == 0x7F) { tls_set_errorx(ctx, "invalid %s: contains DEL char", desc); goto failed; } } return 0; failed: return -1; } static int tls_parse_asn1string(struct tls *ctx, ASN1_STRING *a1str, const char **dst_p, int minchars, int maxchars, const char *desc) { int format, len, ret = -1; unsigned char *data; ASN1_STRING *a1utf = NULL; int ascii_only = 0; char *cstr = NULL; int mbres, mbconvert = -1; *dst_p = NULL; format = ASN1_STRING_type(a1str); data = ASN1_STRING_data(a1str); len = ASN1_STRING_length(a1str); if (len < minchars) { tls_set_errorx(ctx, "invalid %s: string too short", desc); goto failed; } switch (format) { case V_ASN1_NUMERICSTRING: case V_ASN1_VISIBLESTRING: case V_ASN1_PRINTABLESTRING: case V_ASN1_IA5STRING: /* Ascii */ if (len > maxchars) { tls_set_errorx(ctx, "invalid %s: string too long", desc); goto failed; } ascii_only = 1; break; case V_ASN1_T61STRING: /* Latin1 */ mbconvert = MBSTRING_ASC; break; case V_ASN1_BMPSTRING: /* UCS-2 big-endian */ mbconvert = MBSTRING_BMP; break; case V_ASN1_UNIVERSALSTRING: /* UCS-4 big-endian */ mbconvert = MBSTRING_UNIV; break; case V_ASN1_UTF8STRING: /* * UTF-8 - could be used directly if OpenSSL has already * validated the data. ATM be safe and validate here. */ mbconvert = MBSTRING_UTF8; break; default: tls_set_errorx(ctx, "invalid %s: unexpected string type", desc); goto failed; } /* Convert to UTF-8 */ if (mbconvert != -1) { mbres = ASN1_mbstring_ncopy(&a1utf, data, len, mbconvert, B_ASN1_UTF8STRING, minchars, maxchars); if (mbres < 0) { tls_set_error_libssl(ctx, "invalid %s", desc); goto failed; } if (mbres != V_ASN1_UTF8STRING) { tls_set_errorx(ctx, "multibyte conversion failed: expected UTF8 result"); goto failed; } data = ASN1_STRING_data(a1utf); len = ASN1_STRING_length(a1utf); } /* must not allow \0 */ if (memchr(data, 0, len) != NULL) { tls_set_errorx(ctx, "invalid %s: contains NUL", desc); goto failed; } /* no escape codes please */ if (check_invalid_bytes(ctx, data, len, ascii_only, desc) < 0) goto failed; /* copy to new string */ cstr = malloc(len + 1); if (!cstr) { tls_set_error(ctx, "malloc"); goto failed; } memcpy(cstr, data, len); cstr[len] = 0; *dst_p = cstr; ret = len; failed: ASN1_STRING_free(a1utf); return ret; } static int tls_cert_get_dname_string(struct tls *ctx, X509_NAME *name, int nid, const char **str_p, int minchars, int maxchars, const char *desc) { int loc, len; X509_NAME_ENTRY *ne; ASN1_STRING *a1str; *str_p = NULL; loc = X509_NAME_get_index_by_NID(name, nid, -1); if (loc < 0) return 0; ne = X509_NAME_get_entry(name, loc); if (!ne) return 0; a1str = X509_NAME_ENTRY_get_data(ne); if (!a1str) return 0; len = tls_parse_asn1string(ctx, a1str, str_p, minchars, maxchars, desc); if (len < 0) return -1; return 0; } static int tls_load_alt_ia5string(struct tls *ctx, ASN1_IA5STRING *ia5str, struct tls_cert *cert, int slot_type, int minchars, int maxchars, const char *desc) { struct tls_cert_general_name *slot; const char *data; int len; slot = &cert->subject_alt_names[cert->subject_alt_name_count]; len = tls_parse_asn1string(ctx, ia5str, &data, minchars, maxchars, desc); if (len < 0) return 0; /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (len == 1 && data[0] == ' ') { tls_set_errorx(ctx, "invalid %s: single space", desc); return -1; } slot->name_value = data; slot->name_type = slot_type; cert->subject_alt_name_count++; return 0; } static int tls_load_alt_ipaddr(struct tls *ctx, ASN1_OCTET_STRING *bin, struct tls_cert *cert) { struct tls_cert_general_name *slot; void *data; int len; slot = &cert->subject_alt_names[cert->subject_alt_name_count]; len = ASN1_STRING_length(bin); data = ASN1_STRING_data(bin); if (len < 0) { tls_set_errorx(ctx, "negative length for ipaddress"); return -1; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (len == 4) { slot->name_type = TLS_CERT_GNAME_IPv4; } else if (len == 16) { slot->name_type = TLS_CERT_GNAME_IPv6; } else { tls_set_errorx(ctx, "invalid length for ipaddress"); return -1; } slot->name_value = malloc(len); if (slot->name_value == NULL) { tls_set_error(ctx, "malloc"); return -1; } memcpy((void *)slot->name_value, data, len); cert->subject_alt_name_count++; return 0; } /* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_cert_get_altnames(struct tls *ctx, struct tls_cert *cert, X509 *x509_cert) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; GENERAL_NAME *altname; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return 0; count = sk_GENERAL_NAME_num(altname_stack); if (count == 0) { rv = 0; goto out; } cert->subject_alt_names = calloc(sizeof (struct tls_cert_general_name), count); if (cert->subject_alt_names == NULL) { tls_set_error(ctx, "calloc"); goto out; } for (i = 0; i < count; i++) { altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type == GEN_DNS) { rv = tls_load_alt_ia5string(ctx, altname->d.dNSName, cert, TLS_CERT_GNAME_DNS, 1, UB_GNAME_DNS, "dns"); } else if (altname->type == GEN_EMAIL) { rv = tls_load_alt_ia5string(ctx, altname->d.rfc822Name, cert, TLS_CERT_GNAME_EMAIL, 1, UB_GNAME_EMAIL, "email"); } else if (altname->type == GEN_URI) { rv = tls_load_alt_ia5string(ctx, altname->d.uniformResourceIdentifier, cert, TLS_CERT_GNAME_URI, 1, UB_GNAME_URI, "uri"); } else if (altname->type == GEN_IPADD) { rv = tls_load_alt_ipaddr(ctx, altname->d.iPAddress, cert); } else { /* ignore unknown types */ rv = 0; } if (rv < 0) goto out; } rv = 0; out: sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; } static int tls_get_dname(struct tls *ctx, X509_NAME *name, struct tls_cert_dname *dname) { int ret; ret = tls_cert_get_dname_string(ctx, name, NID_commonName, &dname->common_name, 0, UB_COMMON_NAME, "commonName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_countryName, &dname->country_name, 0, UB_COUNTRY_NAME, "countryName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_stateOrProvinceName, &dname->state_or_province_name, 0, UB_STATE_NAME, "stateName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_localityName, &dname->locality_name, 0, UB_LOCALITY_NAME, "localityName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_streetAddress, &dname->street_address, 0, UB_STREET_ADDRESS, "streetAddress"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_organizationName, &dname->organization_name, 0, UB_ORGANIZATION_NAME, "organizationName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_organizationalUnitName, &dname->organizational_unit_name, 0, UB_ORGANIZATIONAL_UNIT_NAME, "organizationalUnitName"); return ret; } static int tls_get_basic_constraints(struct tls *ctx, struct tls_cert *cert, X509 *x509) { BASIC_CONSTRAINTS *bc; int crit; int ret = -1; bc = X509_get_ext_d2i(x509, NID_basic_constraints, &crit, NULL); if (!bc) return 0; cert->ext_set |= TLS_EXT_BASIC; if (crit) cert->ext_crit |= TLS_EXT_BASIC; cert->basic_constraints_ca = bc->ca ? 1 : 0; if (bc->pathlen) { cert->basic_constraints_pathlen = ASN1_INTEGER_get(bc->pathlen); if (cert->basic_constraints_pathlen < 0) { tls_set_error(ctx, "BasicConstraints has invalid pathlen"); goto failed; } } else { cert->basic_constraints_pathlen = -1; } ret = 0; failed: BASIC_CONSTRAINTS_free(bc); return ret; } static uint32_t map_bits(const uint32_t map[][2], uint32_t input) { uint32_t i, out = 0; for (i = 0; map[i][0]; i++) { if (map[i][0] & input) out |= map[i][1]; } return out; } static int tls_get_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509) { static const uint32_t ku_map[][2] = { {KU_DIGITAL_SIGNATURE, KU_DIGITAL_SIGNATURE}, {KU_NON_REPUDIATION, KU_NON_REPUDIATION}, {KU_KEY_ENCIPHERMENT, KU_KEY_ENCIPHERMENT}, {KU_DATA_ENCIPHERMENT, KU_DATA_ENCIPHERMENT}, {KU_KEY_AGREEMENT, KU_KEY_AGREEMENT}, {KU_KEY_CERT_SIGN, KU_KEY_CERT_SIGN}, {KU_CRL_SIGN, KU_CRL_SIGN}, {KU_ENCIPHER_ONLY, KU_ENCIPHER_ONLY}, {KU_DECIPHER_ONLY, KU_DECIPHER_ONLY}, {0, 0}, }; ASN1_BIT_STRING *ku; int crit; ku = X509_get_ext_d2i(x509, NID_key_usage, &crit, NULL); if (!ku) return 0; cert->ext_set |= TLS_EXT_KEY_USAGE; if (crit) cert->ext_crit |= TLS_EXT_KEY_USAGE; ASN1_BIT_STRING_free(ku); cert->key_usage_flags = map_bits(ku_map, X509_get_key_usage(x509)); return 0; } static int tls_get_ext_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509) { static const uint32_t xku_map[][2] = { {XKU_SSL_SERVER, TLS_XKU_SSL_SERVER}, {XKU_SSL_CLIENT, TLS_XKU_SSL_CLIENT}, {XKU_SMIME, TLS_XKU_SMIME}, {XKU_CODE_SIGN, TLS_XKU_CODE_SIGN}, {XKU_SGC, TLS_XKU_SGC}, {XKU_OCSP_SIGN, TLS_XKU_OCSP_SIGN}, {XKU_TIMESTAMP, TLS_XKU_TIMESTAMP}, {XKU_DVCS, TLS_XKU_DVCS}, {0, 0}, }; EXTENDED_KEY_USAGE *xku; int crit; xku = X509_get_ext_d2i(x509, NID_ext_key_usage, &crit, NULL); if (!xku) return 0; sk_ASN1_OBJECT_pop_free(xku, ASN1_OBJECT_free); cert->ext_set |= TLS_EXT_EXTENDED_KEY_USAGE; if (crit) cert->ext_crit |= TLS_EXT_EXTENDED_KEY_USAGE; cert->extended_key_usage_flags = map_bits(xku_map, X509_get_extended_key_usage(x509)); return 0; } static int tls_load_extensions(struct tls *ctx, struct tls_cert *cert, X509 *x509) { int ret; /* * Force libssl to fill extension fields under X509 struct. * Then libtls does not need to parse raw data. */ X509_check_ca(x509); ret = tls_get_basic_constraints(ctx, cert, x509); if (ret == 0) ret = tls_get_key_usage(ctx, cert, x509); if (ret == 0) ret = tls_get_ext_key_usage(ctx, cert, x509); if (ret == 0) ret = tls_cert_get_altnames(ctx, cert, x509); return ret; } static void * tls_calc_fingerprint(struct tls *ctx, X509 *x509, const char *algo, size_t *outlen) { const EVP_MD *md; void *res; int ret; unsigned int tmplen, mdlen; if (outlen) *outlen = 0; if (strcasecmp(algo, "sha1") == 0) { md = EVP_sha1(); } else if (strcasecmp(algo, "sha256") == 0) { md = EVP_sha256(); } else { tls_set_errorx(ctx, "invalid fingerprint algorithm"); return NULL; } mdlen = EVP_MD_size(md); res = malloc(mdlen); if (!res) { tls_set_error(ctx, "malloc"); return NULL; } ret = X509_digest(x509, md, res, &tmplen); if (ret != 1 || tmplen != mdlen) { free(res); tls_set_errorx(ctx, "X509_digest failed"); return NULL; } if (outlen) *outlen = mdlen; return res; } static void check_verify_error(struct tls *ctx, struct tls_cert *cert) { long vres = SSL_get_verify_result(ctx->ssl_conn); if (vres == X509_V_OK) { cert->successful_verify = 1; } else { cert->successful_verify = 0; } } int tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509) { struct tls_cert *cert = NULL; X509_NAME *subject, *issuer; int ret = -1; long version; *cert_p = NULL; version = X509_get_version(x509); if (version < 0) { tls_set_errorx(ctx, "invalid version"); return -1; } subject = X509_get_subject_name(x509); if (!subject) { tls_set_errorx(ctx, "cert does not have subject"); return -1; } issuer = X509_get_issuer_name(x509); if (!issuer) { tls_set_errorx(ctx, "cert does not have issuer"); return -1; } cert = calloc(sizeof *cert, 1); if (!cert) { tls_set_error(ctx, "calloc"); goto failed; } cert->version = version; if (fingerprint_algo) { cert->fingerprint = tls_calc_fingerprint(ctx, x509, fingerprint_algo, &cert->fingerprint_size); if (!cert->fingerprint) goto failed; } ret = tls_get_dname(ctx, subject, &cert->subject); if (ret == 0) ret = tls_get_dname(ctx, issuer, &cert->issuer); if (ret == 0) ret = tls_asn1_parse_time(ctx, X509_get_notBefore(x509), &cert->not_before); if (ret == 0) ret = tls_asn1_parse_time(ctx, X509_get_notAfter(x509), &cert->not_after); if (ret == 0) ret = tls_parse_bigint(ctx, X509_get_serialNumber(x509), &cert->serial); if (ret == 0) ret = tls_load_extensions(ctx, cert, x509); if (ret == 0) { *cert_p = cert; return 0; } failed: tls_cert_free(cert); return ret; } int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo) { X509 *peer = ctx->ssl_peer_cert; int res; *cert_p = NULL; if (!peer) { tls_set_errorx(ctx, "peer does not have cert"); return TLS_NO_CERT; } ERR_clear_error(); res = tls_parse_cert(ctx, cert_p, fingerprint_algo, peer); if (res == 0) check_verify_error(ctx, *cert_p); ERR_clear_error(); return res; } static void tls_cert_free_dname(struct tls_cert_dname *dname) { free((void*)dname->common_name); free((void*)dname->country_name); free((void*)dname->state_or_province_name); free((void*)dname->locality_name); free((void*)dname->street_address); free((void*)dname->organization_name); free((void*)dname->organizational_unit_name); } void tls_cert_free(struct tls_cert *cert) { int i; if (!cert) return; tls_cert_free_dname(&cert->issuer); tls_cert_free_dname(&cert->subject); if (cert->subject_alt_name_count) { for (i = 0; i < cert->subject_alt_name_count; i++) free((void*)cert->subject_alt_names[i].name_value); } free(cert->subject_alt_names); free((void*)cert->serial); free((void*)cert->fingerprint); free(cert); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls_verify.c0000664000401600040160000001427113175113172015114 0ustar cbecbe/* $OpenBSD: tls_verify.c,v 1.7 2015/02/11 06:46:33 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * * 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 "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int tls_match_name(const char *cert_name, const char *name); static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name); static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name); static int tls_match_name(const char *cert_name, const char *name) { const char *cert_domain, *domain, *next_dot; if (strcasecmp(cert_name, name) == 0) return 0; /* Wildcard match? */ if (cert_name[0] == '*') { /* * Valid wildcards: * - "*.domain.tld" * - "*.sub.domain.tld" * - etc. * Reject "*.tld". * No attempt to prevent the use of eg. "*.co.uk". */ cert_domain = &cert_name[1]; /* Disallow "*" */ if (cert_domain[0] == '\0') return -1; /* Disallow "*foo" */ if (cert_domain[0] != '.') return -1; /* Disallow "*.." */ if (cert_domain[1] == '.') return -1; next_dot = strchr(&cert_domain[1], '.'); /* Disallow "*.bar" */ if (next_dot == NULL) return -1; /* Disallow "*.bar.." */ if (next_dot[1] == '.') return -1; domain = strchr(name, '.'); /* No wildcard match against a name with no host part. */ if (name[0] == '.') return -1; /* No wildcard match against a name with no domain part. */ if (domain == NULL || strlen(domain) == 1) return -1; if (strcasecmp(cert_domain, domain) == 0) return 0; } return -1; } /* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; union tls_addr addrbuf; int addrlen, type; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return -1; if (inet_pton(AF_INET, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 4; } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 16; } else { type = GEN_DNS; addrlen = 0; } count = sk_GENERAL_NAME_num(altname_stack); for (i = 0; i < count; i++) { GENERAL_NAME *altname; altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type != type) continue; if (type == GEN_DNS) { void *data; int format, len; format = ASN1_STRING_type(altname->d.dNSName); if (format == V_ASN1_IA5STRING) { data = ASN1_STRING_data(altname->d.dNSName); len = ASN1_STRING_length(altname->d.dNSName); if (len < 0 || len != (int)strlen(data)) { tls_set_errorx(ctx, "error verifying name '%s': " "NUL byte in subjectAltName, " "probably a malicious certificate", name); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (strcmp(data, " ") == 0) { tls_set_error(ctx, "error verifying name '%s': " "a dNSName of \" \" must not be " "used", name); rv = -2; break; } if (tls_match_name(data, name) == 0) { rv = 0; break; } } else { #ifdef DEBUG fprintf(stdout, "%s: unhandled subjectAltName " "dNSName encoding (%d)\n", getprogname(), format); #endif } } else if (type == GEN_IPADD) { unsigned char *data; int datalen; datalen = ASN1_STRING_length(altname->d.iPAddress); data = ASN1_STRING_data(altname->d.iPAddress); if (datalen < 0) { tls_set_errorx(ctx, "Unexpected negative length for an " "IP address: %d", datalen); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (datalen == addrlen && memcmp(data, &addrbuf, addrlen) == 0) { rv = 0; break; } } } sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; } static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name) { X509_NAME *subject_name; char *common_name = NULL; union tls_addr addrbuf; int common_name_len; int rv = -1; subject_name = X509_get_subject_name(cert); if (subject_name == NULL) goto out; common_name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, NULL, 0); if (common_name_len < 0) goto out; common_name = calloc(common_name_len + 1, 1); if (common_name == NULL) goto out; X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name, common_name_len + 1); /* NUL bytes in CN? */ if (common_name_len != (int)strlen(common_name)) { tls_set_errorx(ctx, "error verifying name '%s': " "NUL byte in Common Name field, " "probably a malicious certificate", name); rv = -2; goto out; } if (inet_pton(AF_INET, name, &addrbuf) == 1 || inet_pton(AF_INET6, name, &addrbuf) == 1) { /* * We don't want to attempt wildcard matching against IP * addresses, so perform a simple comparison here. */ if (strcmp(common_name, name) == 0) rv = 0; else rv = -1; goto out; } if (tls_match_name(common_name, name) == 0) rv = 0; out: free(common_name); return rv; } int tls_check_name(struct tls *ctx, X509 *cert, const char *name) { int rv; rv = tls_check_subject_altname(ctx, cert, name); if (rv == 0 || rv == -2) return rv; return tls_check_common_name(ctx, cert, name); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/tls/tls_compat.h0000664000401600040160000000260613175113172015077 0ustar cbecbe #ifndef _USUAL_TLS_COMPAT_H_ #define _USUAL_TLS_COMPAT_H_ #include #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include /* OpenSSL 1.1+ has hidden struct fields */ #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define USE_LIBSSL_INTERNALS #define X509_get_key_usage(x509) ((x509)->ex_kusage) #define X509_get_extended_key_usage(x509) ((x509)->ex_xkusage) #define SSL_CTX_get0_param(ssl_ctx) ((ssl_ctx)->param) #endif /* ecdh_auto is broken - ignores main EC key */ #undef SSL_CTX_set_ecdh_auto /* dh_auto seems fine, but use ours to get DH info */ #undef SSL_CTX_set_dh_auto #ifndef SSL_CTX_set_dh_auto long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff); #endif #ifndef SSL_CTX_set_ecdh_auto long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff); #endif #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM int SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len); #endif #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *buf, int len); #endif /* BoringSSL has no OCSP support */ #ifdef OPENSSL_IS_BORINGSSL #define SSL_CTX_set_tlsext_status_cb(a,b) (1) #define SSL_set_tlsext_status_type(a,b) (1) #endif void tls_compat_cleanup(void); #endif /* USUAL_LIBSSL_FOR_TLS */ #endif /* _USUAL_TLS_COMPAT_H_ */ pgqd/lib/usual/tls/tls_cert.h0000664000401600040160000000477413175113172014561 0ustar cbecbe#ifndef _USUAL_TLS_TLS_CERT_H_ #define _USUAL_TLS_TLS_CERT_H_ #define TLS_CERT_GNAME_DNS 1 #define TLS_CERT_GNAME_IPv4 2 #define TLS_CERT_GNAME_IPv6 3 #define TLS_CERT_GNAME_EMAIL 4 #define TLS_CERT_GNAME_URI 5 #define TLS_KU_DIGITAL_SIGNATURE (1 << 0) #define TLS_KU_NON_REPUDIATION (1 << 1) #define TLS_KU_KEY_ENCIPHERMENT (1 << 2) #define TLS_KU_DATA_ENCIPHERMENT (1 << 3) #define TLS_KU_KEY_AGREEMENT (1 << 4) #define TLS_KU_KEY_CERT_SIGN (1 << 5) #define TLS_KU_CRL_SIGN (1 << 6) #define TLS_KU_ENCIPHER_ONLY (1 << 7) #define TLS_KU_DECIPHER_ONLY (1 << 8) #define TLS_XKU_SSL_SERVER (1 << 0) #define TLS_XKU_SSL_CLIENT (1 << 1) #define TLS_XKU_SMIME (1 << 2) #define TLS_XKU_CODE_SIGN (1 << 3) #define TLS_XKU_OCSP_SIGN (1 << 4) #define TLS_XKU_SGC (1 << 5) #define TLS_XKU_TIMESTAMP (1 << 6) #define TLS_XKU_DVCS (1 << 7) #define TLS_EXT_BASIC (1 << 0) #define TLS_EXT_KEY_USAGE (1 << 1) #define TLS_EXT_EXTENDED_KEY_USAGE (1 << 2) #define TLS_EXT_SUBJECT_ALT_NAME (1 << 3) /* * GeneralName */ struct tls_cert_general_name { const void *name_value; int name_type; }; /* * DistinguishedName */ struct tls_cert_dname { const char *common_name; const char *country_name; const char *state_or_province_name; const char *locality_name; const char *street_address; const char *organization_name; const char *organizational_unit_name; }; struct tls_cert { /* Version number from cert: 0:v1, 1:v2, 2:v3 */ int version; /* did it pass verify? useful when noverifycert is on. */ int successful_verify; /* DistringuishedName for subject */ struct tls_cert_dname subject; /* DistringuishedName for issuer */ struct tls_cert_dname issuer; /* decimal number */ const char *serial; /* Validity times */ time_t not_before; time_t not_after; uint32_t ext_set; uint32_t ext_crit; /* BasicConstraints extension */ int basic_constraints_ca; int basic_constraints_pathlen; /* KeyUsage extension */ uint32_t key_usage_flags; /* ExtendedKeyUsage extension */ uint32_t extended_key_usage_flags; /* SubjectAltName extension */ struct tls_cert_general_name *subject_alt_names; int subject_alt_name_count; /* Fingerprint as raw hash */ const unsigned char *fingerprint; size_t fingerprint_size; }; int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo); void tls_cert_free(struct tls_cert *cert); #ifdef TLS_CERT_INTERNAL_FUNCS int tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509); #endif #endif pgqd/lib/usual/tls/tls_compat.c0000664000401600040160000003664113175113172015100 0ustar cbecbe/* * Compatibility with various libssl implementations. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #ifndef SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE #undef SSLerr #undef X509err #endif #ifndef SSLerr #define SSLerr(a,b) do {} while (0) #define X509err(a,b) do {} while (0) #endif #ifndef SSL_CTX_set_dh_auto #define DH_CLEANUP /* * SKIP primes, used by OpenSSL and PostgreSQL. * * https://tools.ietf.org/html/draft-ietf-ipsec-skip-06 */ static const char file_dh1024[] = "-----BEGIN DH PARAMETERS-----\n" "MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n" "jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n" "ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n" "-----END DH PARAMETERS-----\n"; static const char file_dh2048[] = "-----BEGIN DH PARAMETERS-----\n" "MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n" "89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n" "T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n" "zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n" "Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n" "CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n" "-----END DH PARAMETERS-----\n"; static const char file_dh4096[] = "-----BEGIN DH PARAMETERS-----\n" "MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n" "l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n" "Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n" "Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n" "VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n" "alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n" "sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n" "ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n" "OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n" "AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n" "KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n" "-----END DH PARAMETERS-----\n"; static DH *dh1024, *dh2048, *dh4096; static DH *load_dh_buffer(struct tls *ctx, DH **dhp, const char *buf) { BIO *bio; DH *dh = *dhp; if (dh == NULL) { bio = BIO_new_mem_buf((char *)buf, strlen(buf)); if (bio) { dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); } *dhp = dh; } if (ctx) ctx->used_dh_bits = DH_size(dh) * 8; return dh; } static DH *dh_auto_cb(SSL *s, int is_export, int keylength) { EVP_PKEY *pk; int bits; struct tls *ctx = SSL_get_app_data(s); pk = SSL_get_privatekey(s); if (!pk) return load_dh_buffer(ctx, &dh2048, file_dh2048); bits = EVP_PKEY_bits(pk); if (bits >= 3072) return load_dh_buffer(ctx, &dh4096, file_dh4096); if (bits >= 1536) return load_dh_buffer(ctx, &dh2048, file_dh2048); return load_dh_buffer(ctx, &dh1024, file_dh1024); } static DH *dh_legacy_cb(SSL *s, int is_export, int keylength) { struct tls *ctx = SSL_get_app_data(s); return load_dh_buffer(ctx, &dh1024, file_dh1024); } long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff) { if (onoff == 0) return 1; if (onoff == 2) { SSL_CTX_set_tmp_dh_callback(ctx, dh_legacy_cb); } else { SSL_CTX_set_tmp_dh_callback(ctx, dh_auto_cb); } SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); return 1; } #endif #ifndef SSL_CTX_set_ecdh_auto #define ECDH_CLEANUP /* * Use same curve as EC key, fallback to NIST P-256. */ static EC_KEY *ecdh_cache; #ifdef USE_LIBSSL_INTERNALS static EC_KEY *ecdh_auto_cb(SSL *ssl, int is_export, int keylength) { int last_nid; int nid = 0; EVP_PKEY *pk; EC_KEY *ec; struct tls *ctx = SSL_get_app_data(ssl); /* pick curve from EC key */ pk = SSL_get_privatekey(ssl); if (pk && EVP_PKEY_id(pk) == EVP_PKEY_EC) { ec = EVP_PKEY_get1_EC_KEY(pk); if (ec) { nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); EC_KEY_free(ec); } } /* ssl->tlsext_ellipticcurvelist is empty, nothing else to do... */ if (nid == 0) nid = NID_X9_62_prime256v1; if (ctx) ctx->used_ecdh_nid = nid; if (ecdh_cache) { last_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ecdh_cache)); if (last_nid == nid) return ecdh_cache; EC_KEY_free(ecdh_cache); ecdh_cache = NULL; } ecdh_cache = EC_KEY_new_by_curve_name(nid); return ecdh_cache; } #endif long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff) { if (onoff) { SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); #ifdef USE_LIBSSL_INTERNALS SSL_CTX_set_tmp_ecdh_callback(ctx, ecdh_auto_cb); #endif } return 1; } #endif void tls_compat_cleanup(void) { #ifdef DH_CLEANUP if (dh1024) { DH_free(dh1024); dh1024 = NULL; } if (dh2048) { DH_free(dh2048); dh2048 = NULL; } if (dh4096) { DH_free(dh4096); dh4096 = NULL; } #endif #ifdef ECDH_CLEANUP if (ecdh_cache) { EC_KEY_free(ecdh_cache); ecdh_cache = NULL; } #endif } #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM /* * Load certs for public key from memory. */ int SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *data, int data_len) { pem_password_cb *psw_fn = NULL; void *psw_arg = NULL; X509 *cert; BIO *bio = NULL; int ok; #ifdef USE_LIBSSL_INTERNALS psw_fn = ctx->default_passwd_callback; psw_arg = ctx->default_passwd_callback_userdata; #endif ERR_clear_error(); /* Read from memory */ bio = BIO_new_mem_buf(data, data_len); if (!bio) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB); goto failed; } /* Load primary cert */ cert = PEM_read_bio_X509_AUX(bio, NULL, psw_fn, psw_arg); if (!cert) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB); goto failed; } /* Increments refcount */ ok = SSL_CTX_use_certificate(ctx, cert); X509_free(cert); if (!ok || ERR_peek_error()) goto failed; /* Load extra certs */ ok = SSL_CTX_clear_extra_chain_certs(ctx); while (ok) { cert = PEM_read_bio_X509(bio, NULL, psw_fn, psw_arg); if (!cert) { /* Is it EOF? */ unsigned long err = ERR_peek_last_error(); if (ERR_GET_LIB(err) != ERR_LIB_PEM) break; if (ERR_GET_REASON(err) != PEM_R_NO_START_LINE) break; /* On EOF do successful exit */ BIO_free(bio); ERR_clear_error(); return 1; } /* Does not increment refcount */ ok = SSL_CTX_add_extra_chain_cert(ctx, cert); if (!ok) X509_free(cert); } failed: if (bio) BIO_free(bio); return 0; } #endif #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM /* * Load CA certs for verification from memory. */ int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *data, int data_len) { STACK_OF(X509_INFO) *stack = NULL; X509_STORE *store; X509_INFO *info; int nstack, i, ret = 0, got = 0; BIO *bio; /* Read from memory */ bio = BIO_new_mem_buf(data, data_len); if (!bio) goto failed; /* Parse X509_INFO records */ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); if (!stack) goto failed; /* Loop over stack, add certs and revocation records to store */ store = SSL_CTX_get_cert_store(ctx); nstack = sk_X509_INFO_num(stack); for (i = 0; i < nstack; i++) { info = sk_X509_INFO_value(stack, i); if (info->x509 && !X509_STORE_add_cert(store, info->x509)) goto failed; if (info->crl && !X509_STORE_add_crl(store, info->crl)) goto failed; if (info->x509 || info->crl) got = 1; } ret = got; failed: if (bio) BIO_free(bio); if (stack) sk_X509_INFO_pop_free(stack, X509_INFO_free); if (!ret) X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB); return ret; } #endif #ifndef HAVE_ASN1_TIME_PARSE static int parse2num(const char **str_p, int min, int max) { const char *s = *str_p; if (s && s[0] >= '0' && s[0] <= '9' && s[1] >= '0' && s[1] <= '9') { int val = (s[0] - '0') * 10 + (s[1] - '0'); if (val >= min && val <= max) { *str_p += 2; return val; } } *str_p = NULL; return 0; } int asn1_time_parse(const char *src, size_t len, struct tm *tm, int mode) { char buf[16]; const char *s = buf; int utctime; int year; memset(tm, 0, sizeof *tm); if (mode != 0) return -1; /* * gentime: YYYYMMDDHHMMSSZ * utctime: YYMMDDHHMM(SS)(Z) */ if (len == 15) { utctime = 0; } else if (len > 8 && len < 15) { utctime = 1; } else { return -1; } memcpy(buf, src, len); buf[len] = '\0'; year = parse2num(&s, 0, 99); if (utctime) { if (year < 50) year = 2000 + year; else year = 1900 + year; } else { year = year*100 + parse2num(&s, 0, 99); } tm->tm_year = year - 1900; tm->tm_mon = parse2num(&s, 1, 12) - 1; tm->tm_mday = parse2num(&s, 1, 31); tm->tm_hour = parse2num(&s, 0, 23); tm->tm_min = parse2num(&s, 0, 59); if (utctime) { if (s && s[0] != 'Z' && s[0] != '\0') tm->tm_sec = parse2num(&s, 0, 61); } else { tm->tm_sec = parse2num(&s, 0, 61); } if (s) { if (s[0] == '\0') goto good; if (s[0] == 'Z' && s[1] == '\0') goto good; } return -1; good: return utctime ? V_ASN1_UTCTIME : V_ASN1_GENERALIZEDTIME; } #endif /* HAVE_ASN1_TIME_PARSE */ int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst) { struct tm tm; int res; time_t tval; *dst = 0; if (!asn1time) return 0; if (asn1time->type != V_ASN1_GENERALIZEDTIME && asn1time->type != V_ASN1_UTCTIME) { tls_set_errorx(ctx, "Invalid time object type: %d", asn1time->type); return -1; } res = asn1_time_parse((char*)asn1time->data, asn1time->length, &tm, 0); if (res == -1) { tls_set_errorx(ctx, "Invalid asn1 time"); return -1; } tval = timegm(&tm); if (tval == (time_t)-1) { tls_set_error(ctx, "Cannot convert asn1 time"); return -1; } *dst = tval; return 0; } #else /* !USUAL_LIBSSL_FOR_TLS */ #include /* * Install empty functions when openssl is not available. */ int tls_init(void) { return -1; } void tls_deinit(void) { } const char *tls_backend_version(void) { return "unsupported"; } const char *tls_error(struct tls *_ctx) { return "No TLS support"; } struct tls_config *tls_config_new(void) { return NULL; } void tls_config_free(struct tls_config *_config) {} int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file) { return -1; } int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path) { return -1; } int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, size_t _len) { return -1; } int tls_config_set_cert_file(struct tls_config *_config, const char *_cert_file) { return -1; } int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert, size_t _len) { return -1; } int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers) { return -1; } int tls_config_set_dheparams(struct tls_config *_config, const char *_params) { return -1; } int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_name) { return -1; } int tls_config_set_key_file(struct tls_config *_config, const char *_key_file) { return -1; } int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key, size_t _len) { return -1; } int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char *_blob_file) { return -1; } int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t *_blob, size_t _len) { return -1; } void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols) {} void tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth) {} void tls_config_prefer_ciphers_client(struct tls_config *_config) {} void tls_config_prefer_ciphers_server(struct tls_config *_config) {} void tls_config_insecure_noverifycert(struct tls_config *_config) {} void tls_config_insecure_noverifyname(struct tls_config *_config) {} void tls_config_insecure_noverifytime(struct tls_config *_config) {} void tls_config_verify(struct tls_config *_config) {} void tls_config_verify_client(struct tls_config *_config) {} void tls_config_verify_client_optional(struct tls_config *_config) {} void tls_config_clear_keys(struct tls_config *_config) {} int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr) { return -1; } struct tls *tls_client(void) { return NULL; } struct tls *tls_server(void) { return NULL; } int tls_configure(struct tls *_ctx, struct tls_config *_config) { return -1; } void tls_reset(struct tls *_ctx) {} void tls_free(struct tls *_ctx) {} int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read, int _fd_write) { return -1; } int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket) { return -1; } int tls_connect(struct tls *_ctx, const char *_host, const char *_port) { return -1; } int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, const char *_servername) { return -1; } int tls_connect_servername(struct tls *_ctx, const char *_host, const char *_port, const char *_servername) { return -1; } int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername) { return -1; } int tls_handshake(struct tls *_ctx) { return -1; } ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen) { return -1; } ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen) { return -1; } int tls_close(struct tls *_ctx) { return -1; } int tls_peer_cert_provided(struct tls *ctx) { return 0; } int tls_peer_cert_contains_name(struct tls *ctx, const char *name) { return 0; } const char *tls_peer_cert_hash(struct tls *_ctx) { return NULL; } const char *tls_peer_cert_issuer(struct tls *ctx) { return NULL; } const char *tls_peer_cert_subject(struct tls *ctx) { return NULL; } time_t tls_peer_cert_notbefore(struct tls *ctx) { return (time_t)-1; } time_t tls_peer_cert_notafter(struct tls *ctx) { return (time_t)-1; } const char *tls_conn_version(struct tls *ctx) { return "n/a"; } const char *tls_conn_cipher(struct tls *ctx) { return "n/a"; } uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password) { return NULL; } ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen) { return -1; } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { return -1; } int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *client) { return -1; } int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { return -1; } int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { return -1; } int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size) { return -1; } int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *algo) { *cert_p = NULL; return -1; } void tls_cert_free(struct tls_cert *cert) {} #endif /* !USUAL_LIBSSL_FOR_TLS */ pgqd/lib/usual/config_msvc.h0000664000401600040160000000477013175113172014431 0ustar cbecbe /* Define to 1 if you have the `event_base_new' function. */ #define HAVE_EVENT_BASE_NEW 1 /* Define to 1 if you have the `event_loopbreak' function. */ #define HAVE_EVENT_LOOPBREAK 1 /* Define to 1 if you have the header file. */ #define HAVE_MALLOC_H 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "https://libusual.github.com" /* Define to the full name of this package. */ #define PACKAGE_NAME "libusual" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "libusual 0.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "libusual" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "0.1" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to request cleaner win32 headers. */ #define WIN32_LEAN_AND_MEAN 1 /* Define to max win32 API version (0x0501=XP). */ //#define WINVER 0x0501 #define WINVER 0x0600 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined(_M_IX86) || defined(_M_X64) /* # undef WORDS_BIGENDIAN */ #else #error "Unsupported MSVC target CPU" #endif /* Define to `int' if doesn't define. */ #define gid_t int /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #define inline __inline #endif /* Define to `int' if does not define. */ #define pid_t int /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported directly. */ #ifndef restrict #define restrict #endif /* Define to `int' if doesn't define. */ #define uid_t int #define _CRT_SECURE_NO_WARNINGS 1 #ifndef WIN32 #define WIN32 1 #endif pgqd/lib/usual/list.h0000664000401600040160000000757513175113172013115 0ustar cbecbe/* * Circular doubly linked list implementation. * * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ * * 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. */ /** * @file * Circular doubly linked list. */ #ifndef _USUAL_LIST_H_ #define _USUAL_LIST_H_ #include /** * Structure for both list nodes and heads. * * It is meant to be embedded in parent structure, * which can be acquired with container_of(). */ struct List { /** Pointer to next node or head. */ struct List *next; /** Pointer to previous node or head. */ struct List *prev; }; /** Define and initialize emtpy list head */ #define LIST(var) struct List var = { &var, &var } /** Initialize empty list head. */ static inline void list_init(struct List *list) { list->next = list->prev = list; } /** Is list empty? */ static inline int list_empty(const struct List *list) { return list->next == list; } /** Add item to the start of the list */ static inline struct List *list_prepend(struct List *list, struct List *item) { item->next = list->next; item->prev = list; list->next->prev = item; list->next = item; return item; } /** Add item to the end of the list */ static inline struct List *list_append(struct List *list, struct List *item) { item->next = list; item->prev = list->prev; list->prev->next = item; list->prev = item; return item; } /** Remove item from list */ static inline struct List *list_del(struct List *item) { item->prev->next = item->next; item->next->prev = item->prev; item->next = item->prev = item; return item; } /** Remove first from list and return */ static inline struct List *list_pop(struct List *list) { if (list_empty(list)) return NULL; return list_del(list->next); } /** Get first elem from list */ static inline struct List *list_first(const struct List *list) { if (list_empty(list)) return NULL; return list->next; } /** Get last elem from list */ static inline struct List *list_last(const struct List *list) { if (list_empty(list)) return NULL; return list->prev; } /** Remove first elem from list and return with casting */ #define list_pop_type(list, typ, field) \ (list_empty(list) ? NULL \ : container_of(list_del((list)->next), typ, field)) /** Loop over list */ #define list_for_each(item, list) \ for ((item) = (list)->next; \ (item) != (list); \ (item) = (item)->next) /** Loop over list backwards */ #define list_for_each_reverse(item, list) \ for ((item) = (list)->prev; \ (item) != (list); \ (item) = (item)->prev) /** Loop over list and allow removing item */ #define list_for_each_safe(item, list, tmp) \ for ((item) = (list)->next, (tmp) = (list)->next->next; \ (item) != (list); \ (item) = (tmp), (tmp) = (tmp)->next) /** Loop over list backwards and allow removing item */ #define list_for_each_reverse_safe(item, list, tmp) \ for ((item) = (list)->prev, (tmp) = (list)->prev->prev; \ (item) != (list); \ (item) = (tmp), (tmp) = (tmp)->prev) /** Comparator function signature for list_sort() */ typedef int (*list_cmp_f)(const struct List *a, const struct List *b); /** * Sort list. * * This implementation uses stable merge sort which operates in-place. */ void list_sort(struct List *list, list_cmp_f cmp_func); #endif pgqd/lib/usual/daemon.h0000664000401600040160000000220513175113172013366 0ustar cbecbe/** @file * Daemonization & pidfile handling. */ /* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_DAEMON_H_ #define _USUAL_DAEMON_H_ #include /** * Read a pid from pidfile and send a signal to it. */ bool signal_pidfile(const char *pidfile, int sig); /** * Daemonize process and write pidfile. */ void daemonize(const char *pidfile, bool go_background); #endif pgqd/lib/usual/string.h0000664000401600040160000001437013175113172013437 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file * Theme include for strings. */ #ifndef _USUAL_STRING_H_ #define _USUAL_STRING_H_ #include #include /** * @name List of strings. * @{ */ /** Callback signature */ typedef bool (*str_cb)(void *arg, const char *s); struct StrList; /** Allocate new string list */ struct StrList *strlist_new(CxMem *ca); /** Free string string */ void strlist_free(struct StrList *slist); /** Check if empty */ bool strlist_empty(struct StrList *slist); /** Append copy of string. */ bool strlist_append(struct StrList *slist, const char *str); /** Append reference, strlist now owns it. */ bool strlist_append_ref(struct StrList *slist, const char *str); /** Call function on each element */ bool strlist_foreach(const struct StrList *slist, str_cb cb_func, void *cb_arg); /** Remove and return first element */ const char *strlist_pop(struct StrList *slist); /* @} */ /** Parse comma-separated elements from string and launch callback for each of them. */ bool parse_word_list(const char *s, str_cb cb_func, void *cb_arg); #ifndef HAVE_STRNLEN #define strnlen(a,b) usual_strnlen(a,b) /** Compat: determine the length of a fixed-size string */ size_t strnlen(const char *string, size_t maxlen); #endif #ifndef HAVE_STRLCPY #define strlcpy(a,b,c) usual_strlcpy(a,b,c) /** Compat: Safely copy string to fixed-length buffer. */ size_t strlcpy(char *dst, const char *src, size_t n); #endif #ifndef HAVE_STRLCAT #define strlcat(a,b,c) usual_strlcat(a,b,c) /** Compat: Safely append string to fixed-length buffer. */ size_t strlcat(char *dst, const char *src, size_t n); #endif #undef strpcpy #define strpcpy(a,b,c) usual_strpcpy(a,b,c) /** * Safe string copy. * * Returns pointer to end of string in dst or NULL * if truncation occured. Destination will be * zero-terminated unless dstlen is 0. */ char *strpcpy(char *dst, const char *src, size_t dstlen); #undef strpcat #define strpcat(a,b,c) usual_strpcat(a,b,c) /** * Safe string concat. * * Returns pointer to end of string in dst or NULL if truncation occured. * Destination will be zero-terminated, unless dstlen is 0 or existing * contents were not zero-terminated. */ char *strpcat(char *dst, const char *src, size_t dstlen); #ifndef HAVE_MEMRCHR #define memrchr(a,b,c) usual_memrchr(a,b,c) /** Compat: find byte in reverse direction */ void *memrchr(const void *s, int c, size_t n); #endif #ifndef HAVE_MEMMEM #define memmem(a,b,c,d) usual_memmem(a,b,c,d) /** Compat: find memory area */ void *memmem(const void *s, size_t slen, const void *q, size_t qlen); #endif #ifndef HAVE_MEMPCPY #define mempcpy(a,b,c) usual_mempcpy(a,b,c) /** Copy memory, return pointer to end. */ void *mempcpy(void *dst, const void *src, size_t len); #endif /** Return position to first byte that is in 'find'. */ void *mempbrk(const void *data, size_t dlen, const void *find, size_t flen); /** Return number of bytes where none are in reject. */ size_t memcspn(const void *data, size_t dlen, const void *reject, size_t rlen); /** Return number of bytes where all are in accept. */ size_t memspn(const void *data, size_t dlen, const void *accept, size_t alen); #ifndef HAVE_BASENAME #undef basename #define basename(a) usual_basename(a) /** Compat: Return pointer to last non-path element. Never modifies path, returns either pointer inside path or static buffer. */ const char *basename(const char *path); #endif #ifndef HAVE_DIRNAME #undef dirname #define dirname(a) usual_dirname(a) /** Compat: Return directory part of pathname. Never modifies path, returns either pointer inside path or static buffer. */ const char *dirname(const char *path); #endif #ifndef HAVE_EXPLICIT_BZERO #undef explicit_bzero #define explicit_bzero(a,b) usual_explicit_bzero(a,b) /** Definitely clear memory */ void explicit_bzero(void *buf, size_t len); #endif /* * strerror, strerror_r */ #ifdef WIN32 const char *win32_strerror(int e); /** Compat: strerror() for win32 */ #define strerror(x) win32_strerror(x) #endif const char *usual_strerror_r(int e, char *dst, size_t dstlen); /** Compat: Provide GNU-style API: const char *strerror_r(int e, char *dst, size_t dstlen) */ #define strerror_r(a,b,c) usual_strerror_r(a,b,c) /** strtod() that uses '.' as decimal separator */ double strtod_dot(const char *s, char **tokend); /** Convert double to string with '.' as decimal separator */ ssize_t dtostr_dot(char *buf, size_t buflen, double val); #ifndef HAVE_STRTONUM #define strtonum(a,b,c,d) usual_strtonum(a,b,c,d) /** * Convert string to integer, check limits. * * Accepts only decimal numbers, no octal or hex. Allows leading whitespace, * but not tailing. * * On success, returns value that is minval <= res <= maxval. If errstr_p is given * it stores NULL there. Keeps old errno value. * * On error, returns 0, sets errno, and if errstr_p is given, stores error reason there. */ long long strtonum(const char *s, long long minval, long long maxval, const char **errstr_p); #endif #ifndef HAVE_STRSEP #undef strsep #define strsep(a,b) usual_strsep(a,b) /** * Return next token from string. * * Tokens are separated by delim chars * Modifies string in-place. */ char *strsep(char **stringp, const char *delim); #endif #ifndef HAVE_ASPRINTF #define asprintf(dst_p, fmt, ...) usual_asprintf(dst_p, fmt, __VA_ARGS__) int asprintf(char **dst_p, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_VASPRINTF #define vasprintf(dst_p, fmt, ap) usual_vasprintf(dst_p, fmt, ap) int vasprintf(char **dst_p, const char *fmt, va_list ap) _PRINTF(2, 0); #endif #endif pgqd/lib/usual/pthread.c0000664000401600040160000000460513175113172013553 0ustar cbecbe/* * Pthreads compat. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifndef HAVE_PTHREAD_H #ifdef WIN32 /* * basic pthreads for win32. */ struct _w32thread { void *(*fn)(void *); void *arg; }; static DWORD WINAPI w32launcher(LPVOID arg) { struct _w32thread *info = arg; info->fn(info->arg); free(info); return 0; } int pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*fn)(void *), void *arg) { struct _w32thread *info = calloc(1, sizeof(*info)); if (!info) return -1; info->fn = fn; info->arg = arg; *t = CreateThread(NULL, 0, w32launcher, info, 0, NULL); if (*t == NULL) return -1; return 0; } int pthread_join(pthread_t *t, void **ret) { if (WaitForSingleObject(*t, INFINITE) != WAIT_OBJECT_0) return -1; CloseHandle(*t); return 0; } int pthread_mutex_init(pthread_mutex_t *lock, void *unused) { *lock = CreateMutex(NULL, FALSE, NULL); if (*lock == NULL) return -1; return 0; } int pthread_mutex_destroy(pthread_mutex_t *lock) { if (*lock) { CloseHandle(*lock); *lock = NULL; } return 0; } int pthread_mutex_lock(pthread_mutex_t *lock) { if (WaitForSingleObject(*lock, INFINITE) != WAIT_OBJECT_0) return -1; return 0; } int pthread_mutex_unlock(pthread_mutex_t *lock) { if (!ReleaseMutex(*lock)) return -1; return 0; } #ifdef INIT_ONCE_STATIC_INIT typedef void (*once_posix_cb_t)(void); static BOOL once_wrapper(PINIT_ONCE once, void *arg, void **ctx) { once_posix_cb_t cb = arg; arg(); return TRUE; } int pthread_once(pthread_once_t *once, void (*once_func)(void)) { return InitOnceExecuteOnce(once, once_wrapper, once_func, NULL) ? 0 : -1; } #endif #endif /* win32 */ #endif /* !HAVE_PTHREAD_H */ pgqd/lib/usual/signal.c0000664000401600040160000000545713175113172013407 0ustar cbecbe/* * Signal compat. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * alarm() for win32 */ #ifdef WIN32 struct AlarmCtx { struct sigaction sa; HANDLE event; HANDLE thread; int secs; }; static volatile struct AlarmCtx actx; static DWORD WINAPI w32_alarm_thread(LPVOID arg) { DWORD wres; unsigned msecs; loop: if (actx.secs > 0) { msecs = actx.secs * 1000; } else { msecs = INFINITE; } wres = WaitForSingleObject(actx.event, msecs); if (wres == WAIT_OBJECT_0) { goto loop; } else if (wres == WAIT_TIMEOUT) { actx.secs = 0; if (actx.sa.sa_handler) actx.sa.sa_handler(SIGALRM); goto loop; } else { Sleep(1000); goto loop; } return 0; } unsigned int alarm(unsigned int secs) { actx.secs = secs; /* create event */ if (!actx.event) { actx.event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!actx.event) return 0; } /* create or notify thread */ if (!actx.thread) { actx.thread = CreateThread(NULL, 0, w32_alarm_thread, NULL, 0, NULL); } else { SetEvent(actx.event); } return 0; } #endif #ifndef HAVE_SIGACTION int sigaction(int sig, const struct sigaction *sa, struct sigaction *old) { #ifdef WIN32 if (sig == SIGALRM) { if (old) *old = actx.sa; if (sa) actx.sa = *sa; else actx.sa.sa_handler = NULL; return 0; } #endif old->sa_handler = signal(sig, sa->sa_handler); if (old->sa_handler == SIG_ERR) return -1; return 0; } #endif #ifdef WIN32 /* Only sig=0 is supported, to detect if process is running (ESRCH->not) */ int kill(int pid, int sig) { HANDLE hProcess; DWORD exitCode; int ret = 0; /* handle only sig == 0 */ if (sig != 0) { errno = EINVAL; return -1; } hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if (hProcess == NULL) { if (GetLastError() == ERROR_INVALID_PARAMETER) ret = ESRCH; else ret = EPERM; } else { /* OpenProcess may succed for exited processes */ if (GetExitCodeProcess(hProcess, &exitCode)) { if (exitCode != STILL_ACTIVE) ret = ESRCH; } CloseHandle(hProcess); } if (ret) { errno = ret; return -1; } else return 0; } #endif pgqd/lib/usual/getopt.h0000664000401600040160000000670013175113172013431 0ustar cbecbe/* * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /** * @file * getopt compat. * * This module provides getopt() and getopt_long(). */ #ifndef _USUAL_GETOPT_H_ #define _USUAL_GETOPT_H_ #include #ifndef NEED_USUAL_GETOPT #if !defined(HAVE_GETOPT_H) || !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_LONG) #define NEED_USUAL_GETOPT #endif #endif #ifndef NEED_USUAL_GETOPT /* Use system getopt */ #include #else /* NEED_USUAL_GETOPT */ /* avoid name collision */ #define optarg usual_optarg #define opterr usual_opterr #define optind usual_optind #define optopt usual_optopt #define getopt(a,b,c) usual_getopt(a,b,c) #define getopt_long(a,b,c,d,e) usual_getopt_long(a,b,c,d,e) /** argument to current option, or NULL if it has none */ extern char *optarg; /** Current position in arg string. Starts from 1. Setting to 0 resets state. */ extern int optind; /** whether getopt() should print error messages on problems. Default: 1. */ extern int opterr; /** Option char which caused error */ extern int optopt; /** long option takes no argument */ #define no_argument 0 /** long option requires argument */ #define required_argument 1 /** long option has optional argument */ #define optional_argument 2 /** Long option description */ struct option { /** name of long option */ const char *name; /** * whether option takes an argument. * One of no_argument, required_argument, and optional_argument. */ int has_arg; /** if not NULL, set *flag to val when option found */ int *flag; /** if flag not NULL, value to set *flag to; else return value */ int val; }; /** Compat: getopt */ int getopt(int argc, char *argv[], const char *options); /** Compat: getopt_long */ int getopt_long(int argc, char *argv[], const char *options, const struct option *longopts, int *longindex); /** Compat: getopt_long_only */ int getopt_long_only(int nargc, char *argv[], const char *options, const struct option *long_options, int *idx); #endif /* NEED_USUAL_GETOPT */ #endif /* !_USUAL_GETOPT_H_ */ pgqd/lib/usual/cxalloc.h0000664000401600040160000000765513175113172013566 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Context-based Memory Allocator. * * The idea is that each data structure is given a context to allocate from, * and it can create subcontext for that which can be specific allocation * pattern that matches the data structure. * * It is slightly more work to use than palloc (PostgreSQL) or talloc (Samba), * but it avoids the need to have big fully-featured framework that does * everything at once. * * Instead you have small task-specific allocators, and you can always fall * back to raw malloc if you want to valgrind the code. * * Potential variants: * - fully-featured pooled * - randomly failing * - logging * - locking * - guard signatures * - palloc / talloc like API */ #ifndef _USUAL_CXALLOC_H_ #define _USUAL_CXALLOC_H_ #include /** * Ops for allocator that takes context. * * NB! - they are not equivalent to cx_* API. The cx_* * functions do additional sanitizing. */ struct CxOps { /** * Allocate memory. * len will not be 0. */ void *(*c_alloc)(void *ctx, size_t len); /** * Resize existing allocation. * Both p and len will not be 0 */ void *(*c_realloc)(void *ctx, void *p, size_t len); /** * Free existing allocation. * p will not be 0 */ void (*c_free)(void *ctx, const void *p); /** * Release all memory in context. * This is not supported by all allocators. */ void (*c_destroy)(void *ctx); }; /** * Memory allocation context. */ struct CxMem { const struct CxOps *ops; void *ctx; }; /** Shortcut to const CxMem */ typedef const struct CxMem CxMem; /* * Basic operations on allocation context. */ /** * Allocate memory from context. * * Returns NULL if no memory or len == 0. */ void *cx_alloc(CxMem *cx, size_t len) _MALLOC; /** * Change existing allocation. * * If ptr is NULL it creates new allocation. * If len is 0 it frees the memory. */ void *cx_realloc(CxMem *cx, void *ptr, size_t len); /** * Free existing allocation. * * Does nothing if ptr is NULL. */ void cx_free(CxMem *cx, const void *ptr); /** * Release all memory allocated in context. * * Should be called only on contexts that support it. */ void cx_destroy(CxMem *cx); /** Allocate and zero-fill memory */ void *cx_alloc0(CxMem *cx, size_t len) _MALLOC; /** Allocate and copy */ void *cx_memdup(CxMem *cx, const void *src, size_t len) _MALLOC; /** Allocate and copy string */ void *cx_strdup(CxMem *cx, const char *str) _MALLOC; /** Print to allocated string, return length or -1 on error. */ int cx_asprintf(CxMem *cx, char **dst_p, const char *fmt, ...) _PRINTF(3, 4); /** Print to allocated string, return length or -1 on error */ int cx_vasprintf(CxMem *cx, char **dst_p, const char *fmt, va_list ap) _PRINTF(3, 0); /** Print to allocated string, return new string or NULL on error */ char *cx_sprintf(CxMem *cx, const char *fmt, ...) _PRINTF(2, 3); /** Print to allocated string, return new string or NULL on error */ char *cx_vsprintf(CxMem *cx, const char *fmt, va_list ap) _PRINTF(2, 0); /** Allocator that uses libc malloc/realloc/free */ extern CxMem cx_libc_allocator; /** Default allocator */ #ifndef USUAL_ALLOC #define USUAL_ALLOC (&cx_libc_allocator) #endif #endif pgqd/lib/usual/base.c0000664000401600040160000000330313175113172013030 0ustar cbecbe/* * Basic C environment. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #if defined(HAVE_MALLOC_H) && defined(__darwin__) #include #endif /* define posix_memalign() only when possible to emulate */ #if !defined(HAVE_POSIX_MEMALIGN) \ && (defined(HAVE_MEMALIGN) || defined(HAVE_VALLOC)) int posix_memalign(void **ptr_p, size_t align, size_t len) { void *p; int ret, old_errno = errno; #ifdef HAVE_MEMALIGN p = memalign(align, len); #else /* !HAVE_MEMALIGN */ #ifdef HAVE_VALLOC /* assuming less than pagesize alignment */ p = valloc(len); #endif /* !VALLOC */ #endif /* !MEMALIGN */ *ptr_p = p; if (p) return 0; /* on error restore old errno */ ret = errno; errno = old_errno; return ret; } #endif #ifndef HAVE_REALLOCARRAY void *reallocarray(void *p, size_t count, size_t size) { size_t total; if (!safe_mul_size(&total, count, size)) { errno = ENOMEM; return NULL; } return realloc(p, total); } #endif pgqd/lib/usual/err.h0000664000401600040160000000375013175113172012721 0ustar cbecbe/* * Cmdline error reporting. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Error printing for command-line utilities. */ #ifndef _USUAL_ERR_H_ #define _USUAL_ERR_H_ #include #ifdef HAVE_ERR_H #include #endif #ifndef HAVE_ERR /** Print formatted message and strerror(errno) to stderr and exit with given error code */ void err(int e, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_ERRX /** Print formatted message to stderr and exit with given error code */ void errx(int e, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_WARN /** Print formatted message and strerror(errno) to stderr */ void warn(const char *fmt, ...) _PRINTF(1, 2); #endif #ifndef HAVE_WARNX /** Print formatted message to stderr */ void warnx(const char *fmt, ...) _PRINTF(1, 2); #endif #ifndef HAVE_SETPROGNAME /** Set program name to that will printed as prefix to error messages */ void setprogname(const char *s); #endif #ifndef HAVE_GETPROGNAME /** Return program name set with @ref setprogname */ const char *getprogname(void); #endif /** Malloc that exits on failure */ void *xmalloc(size_t len); /** Realloc that exits on failure */ void *xrealloc(void *p, size_t len); /** strdup that exits on failure */ char *xstrdup(const char *s); #endif pgqd/lib/usual/strpool.h0000664000401600040160000000350313175113172013627 0ustar cbecbe/* * Pool for shared strings. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Storage for shared strings. * * This provides refcounted searchable string pool for cases * where lot of objects reference same strings. */ #ifndef _USUAL_STRPOOL_H_ #define _USUAL_STRPOOL_H_ #include /** Handle for the pool */ struct StrPool; /** Pooled String */ struct PStr { /** Parent pool */ struct StrPool *pool; /** String length */ size_t len; /** Reference count */ int refcnt; /** Zero-terminated value */ char str[FLEX_ARRAY]; }; /** Create new pool */ struct StrPool *strpool_create(CxMem *ca); /** Release pool */ void strpool_free(struct StrPool *sp); /** Return either existing or new PStr for given value */ struct PStr *strpool_get(struct StrPool *sp, const char *str, ssize_t len); /** Increase reference count for existing PStr */ void strpool_incref(struct PStr *str); /** Decrease reference count for existing PStr */ void strpool_decref(struct PStr *str); /** Return count of strings in the pool */ int strpool_total(struct StrPool *sp); #endif pgqd/lib/usual/json.c0000664000401600040160000011307113175113172013073 0ustar cbecbe/* * Read and write JSON. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #define TYPE_BITS 3 #define TYPE_MASK ((1 << TYPE_BITS) - 1) #define UNATTACHED ((struct JsonValue *)(1 << TYPE_BITS)) #define JSON_MAX_KEY (1024*1024) #define NUMBER_BUF 100 #define JSON_MAXINT ((1LL << 53) - 1) #define JSON_MININT (-(1LL << 53) + 1) /* * Common struct for all JSON values */ struct JsonValue { /* actual value for simple types */ union { double v_float; /* float */ int64_t v_int; /* int */ bool v_bool; /* bool */ size_t v_size; /* str/list/dict */ } u; /* pointer to next elem and type in low bits */ uintptr_t v_next_and_type; }; /* * List container. */ struct ValueList { struct JsonValue *first; struct JsonValue *last; struct JsonValue **array; }; /* * Extra data for list/dict. */ struct JsonContainer { /* parent container */ struct JsonValue *c_parent; /* main context for child alloc */ struct JsonContext *c_ctx; /* child elements */ union { struct CBTree *c_dict; struct ValueList c_list; } u; }; #define DICT_EXTRA (offsetof(struct JsonContainer, u.c_dict) + sizeof(struct CBTree *)) #define LIST_EXTRA (sizeof(struct JsonContainer)) /* * Allocation context. */ struct JsonContext { CxMem *pool; unsigned int options; /* parse state */ struct JsonValue *parent; struct JsonValue *cur_key; struct JsonValue *top; const char *lasterr; char errbuf[128]; int64_t linenr; }; struct RenderState { struct MBuf *dst; unsigned int options; }; /* * Parser states */ enum ParseState { S_INITIAL_VALUE = 1, S_LIST_VALUE, S_LIST_VALUE_OR_CLOSE, S_LIST_COMMA_OR_CLOSE, S_DICT_KEY, S_DICT_KEY_OR_CLOSE, S_DICT_COLON, S_DICT_VALUE, S_DICT_COMMA_OR_CLOSE, S_PARENT, S_DONE, MAX_STATES, }; /* * Tokens that change state. */ enum TokenTypes { T_STRING, T_OTHER, T_COMMA, T_COLON, T_OPEN_DICT, T_OPEN_LIST, T_CLOSE_DICT, T_CLOSE_LIST, MAX_TOKENS }; /* * 4-byte ints for small string tokens. */ #define C_NULL FOURCC('n','u','l','l') #define C_TRUE FOURCC('t','r','u','e') #define C_ALSE FOURCC('a','l','s','e') /* * Signature for render functions. */ typedef bool (*render_func_t)(struct RenderState *rs, struct JsonValue *jv); static bool render_any(struct RenderState *rs, struct JsonValue *jv); /* * Header manipulation */ static inline enum JsonValueType get_type(struct JsonValue *jv) { return jv->v_next_and_type & TYPE_MASK; } static inline bool has_type(struct JsonValue *jv, enum JsonValueType type) { if (!jv) return false; return get_type(jv) == type; } static inline struct JsonValue *get_next(struct JsonValue *jv) { return (struct JsonValue *)(jv->v_next_and_type & ~(uintptr_t)TYPE_MASK); } static inline void set_next(struct JsonValue *jv, struct JsonValue *next) { jv->v_next_and_type = (uintptr_t)next | get_type(jv); } static inline bool is_unattached(struct JsonValue *jv) { return get_next(jv) == UNATTACHED; } static inline void *get_extra(struct JsonValue *jv) { return (void *)(jv + 1); } static inline char *get_cstring(struct JsonValue *jv) { enum JsonValueType type = get_type(jv); if (type != JSON_STRING) return NULL; return get_extra(jv); } /* * Collection header manipulation. */ static inline struct JsonContainer *get_container(struct JsonValue *jv) { enum JsonValueType type = get_type(jv); if (type != JSON_DICT && type != JSON_LIST) return NULL; return get_extra(jv); } static inline void set_parent(struct JsonValue *jv, struct JsonValue *parent) { struct JsonContainer *c = get_container(jv); if (c) c->c_parent = parent; } static inline struct JsonContext *get_context(struct JsonValue *jv) { struct JsonContainer *c = get_container(jv); return c ? c->c_ctx : NULL; } static inline struct CBTree *get_dict_tree(struct JsonValue *jv) { struct JsonContainer *c; if (has_type(jv, JSON_DICT)) { c = get_container(jv); return c->u.c_dict; } return NULL; } static inline struct ValueList *get_list_vlist(struct JsonValue *jv) { struct JsonContainer *c; if (has_type(jv, JSON_LIST)) { c = get_container(jv); return &c->u.c_list; } return NULL; } /* * Random helpers */ /* copy and return final pointer */ static inline char *plain_copy(char *dst, const char *src, const char *endptr) { if (src < endptr) { memcpy(dst, src, endptr - src); return dst + (endptr - src); } return dst; } /* error message on context */ _PRINTF(2,0) static void format_err(struct JsonContext *ctx, const char *errmsg, va_list ap) { char buf[128]; if (ctx->lasterr) return; vsnprintf(buf, sizeof(buf), errmsg, ap); snprintf(ctx->errbuf, sizeof(ctx->errbuf), "Line #%" PRIi64 ": %s", ctx->linenr, buf); ctx->lasterr = ctx->errbuf; } /* set message and return false */ _PRINTF(2,3) static bool err_false(struct JsonContext *ctx, const char *errmsg, ...) { va_list ap; va_start(ap, errmsg); format_err(ctx, errmsg, ap); va_end(ap); return false; } /* set message and return NULL */ _PRINTF(2,3) static void *err_null(struct JsonContext *ctx, const char *errmsg, ...) { va_list ap; va_start(ap, errmsg); format_err(ctx, errmsg, ap); va_end(ap); return NULL; } /* callback for cbtree, returns key bytes */ static size_t get_key_data_cb(void *dictptr, void *keyptr, const void **dst_p) { struct JsonValue *key = keyptr; *dst_p = get_cstring(key); return key->u.v_size; } /* add elemnt to list */ static void real_list_append(struct JsonValue *list, struct JsonValue *elem) { struct ValueList *vlist; vlist = get_list_vlist(list); if (vlist->last) { set_next(vlist->last, elem); } else { vlist->first = elem; } vlist->last = elem; vlist->array = NULL; list->u.v_size++; } /* add key to tree */ static bool real_dict_add_key(struct JsonContext *ctx, struct JsonValue *dict, struct JsonValue *key) { struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return err_false(ctx, "Expect dict"); if (json_value_size(key) > JSON_MAX_KEY) return err_false(ctx, "Too large key"); dict->u.v_size++; if (!cbtree_insert(tree, key)) return err_false(ctx, "Key insertion failed"); return true; } /* create basic value struct, link to stuctures */ static struct JsonValue *mk_value(struct JsonContext *ctx, enum JsonValueType type, size_t extra, bool attach) { struct JsonValue *val; struct JsonContainer *col = NULL; if (!ctx) return NULL; val = cx_alloc(ctx->pool, sizeof(struct JsonValue) + extra); if (!val) return err_null(ctx, "No memory"); if ((uintptr_t)val & TYPE_MASK) return err_null(ctx, "Unaligned pointer"); /* initial value */ val->v_next_and_type = type; val->u.v_int = 0; if (type == JSON_DICT || type == JSON_LIST) { col = get_container(val); col->c_ctx = ctx; col->c_parent = NULL; if (type == JSON_DICT) { col->u.c_dict = cbtree_create(get_key_data_cb, NULL, val, ctx->pool); if (!col->u.c_dict) return err_null(ctx, "No memory"); } else { memset(&col->u.c_list, 0, sizeof(col->u.c_list)); } } /* independent JsonValue? */ if (!attach) { set_next(val, UNATTACHED); return val; } /* attach to parent */ if (col) col->c_parent = ctx->parent; /* attach to previous value */ if (has_type(ctx->parent, JSON_DICT)) { if (ctx->cur_key) { set_next(ctx->cur_key, val); ctx->cur_key = NULL; } else { ctx->cur_key = val; } } else if (has_type(ctx->parent, JSON_LIST)) { real_list_append(ctx->parent, val); } else if (!ctx->top) { ctx->top = val; } else { return err_null(ctx, "Only one top element is allowed"); } return val; } static void prepare_array(struct JsonValue *list) { struct JsonContainer *c; struct JsonValue *val; struct ValueList *vlist; size_t i; vlist = get_list_vlist(list); if (vlist->array) return; c = get_container(list); vlist->array = cx_alloc(c->c_ctx->pool, list->u.v_size * sizeof(struct JsonValue *)); if (!vlist->array) return; val = vlist->first; for (i = 0; i < list->u.v_size && val; i++) { vlist->array[i] = val; val = get_next(val); } } /* * Parsing code starts */ /* create and change context */ static bool open_container(struct JsonContext *ctx, enum JsonValueType type, unsigned int extra) { struct JsonValue *jv; jv = mk_value(ctx, type, extra, true); if (!jv) return false; ctx->parent = jv; ctx->cur_key = NULL; return true; } /* close and change context */ static enum ParseState close_container(struct JsonContext *ctx, enum ParseState state) { struct JsonContainer *c; if (state != S_PARENT) return (int)err_false(ctx, "close_container bug"); c = get_container(ctx->parent); if (!c) return (int)err_false(ctx, "invalid parent"); ctx->parent = c->c_parent; ctx->cur_key = NULL; if (has_type(ctx->parent, JSON_DICT)) { return S_DICT_COMMA_OR_CLOSE; } else if (has_type(ctx->parent, JSON_LIST)) { return S_LIST_COMMA_OR_CLOSE; } return S_DONE; } /* parse 4-char token */ static bool parse_char4(struct JsonContext *ctx, const char **src_p, const char *end, uint32_t t_exp, enum JsonValueType type, bool val) { const char *src; uint32_t t_got; struct JsonValue *jv; src = *src_p; if (src + 4 > end) return err_false(ctx, "Unexpected end of token"); memcpy(&t_got, src, 4); if (t_exp != t_got) return err_false(ctx, "Invalid token"); jv = mk_value(ctx, type, 0, true); if (!jv) return false; jv->u.v_bool = val; *src_p += 4; return true; } /* parse int or float */ static bool parse_number(struct JsonContext *ctx, const char **src_p, const char *end) { const char *start, *src; enum JsonValueType type = JSON_INT; char *tokend = NULL; char buf[NUMBER_BUF]; size_t len; struct JsonValue *jv; double v_float = 0; int64_t v_int = 0; /* scan & copy */ start = src = *src_p; for (; src < end; src++) { if (*src >= '0' && *src <= '9') { } else if (*src == '+' || *src == '-') { } else if (*src == '.' || *src == 'e' || *src == 'E') { type = JSON_FLOAT; } else { break; } } len = src - start; if (len >= NUMBER_BUF) goto failed; memcpy(buf, start, len); buf[len] = 0; /* now parse */ errno = 0; tokend = buf; if (type == JSON_FLOAT) { v_float = strtod_dot(buf, &tokend); if (*tokend != 0 || errno || !isfinite(v_float)) goto failed; } else if (len < 8) { v_int = strtol(buf, &tokend, 10); if (*tokend != 0 || errno) goto failed; } else { v_int = strtoll(buf, &tokend, 10); if (*tokend != 0 || errno || v_int < JSON_MININT || v_int > JSON_MAXINT) goto failed; } /* create value struct */ jv = mk_value(ctx, type, 0, true); if (!jv) return false; if (type == JSON_FLOAT) { jv->u.v_float = v_float; } else { jv->u.v_int = v_int; } *src_p = src; return true; failed: if (!errno) errno = EINVAL; return err_false(ctx, "Number parse failed"); } /* * String parsing */ static int parse_hex(const char *s, const char *end) { int v = 0, c, i, x; if (s + 4 > end) return -1; for (i = 0; i < 4; i++) { c = s[i]; if (c >= '0' && c <= '9') { x = c - '0'; } else if (c >= 'a' && c <= 'f') { x = c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { x = c - 'A' + 10; } else { return -1; } v = (v << 4) | x; } return v; } /* process \uXXXX escapes, merge surrogates */ static bool parse_uescape(struct JsonContext *ctx, char **dst_p, char *dstend, const char **src_p, const char *end) { int c, c2; const char *src = *src_p; c = parse_hex(src, end); if (c <= 0) return err_false(ctx, "Invalid hex escape"); src += 4; if (c >= 0xD800 && c <= 0xDFFF) { /* first surrogate */ if (c >= 0xDC00) return err_false(ctx, "Invalid UTF16 escape"); if (src + 6 > end) return err_false(ctx, "Invalid UTF16 escape"); /* second surrogate */ if (src[0] != '\\' || src[1] != 'u') return err_false(ctx, "Invalid UTF16 escape"); c2 = parse_hex(src + 2, end); if (c2 < 0xDC00 || c2 > 0xDFFF) return err_false(ctx, "Invalid UTF16 escape"); c = 0x10000 + ((c & 0x3FF) << 10) + (c2 & 0x3FF); src += 6; } /* now write char */ if (!utf8_put_char(c, dst_p, dstend)) return err_false(ctx, "Invalid UTF16 escape"); *src_p = src; return true; } #define meta_string(c) (((c) == '"' || (c) == '\\' || (c) == '\0' || \ (c) == '\n' || ((c) & 0x80) != 0) ? 1 : 0) static const uint8_t string_examine_chars[] = INTMAP256_CONST(meta_string); /* look for string end, validate contents */ static bool scan_string(struct JsonContext *ctx, const char *src, const char *end, const char **str_end_p, bool *hasesc_p, int64_t *nlines_p) { bool hasesc = false; int64_t lines = 0; unsigned int n; bool check_utf8 = true; if (ctx->options & JSON_PARSE_IGNORE_ENCODING) check_utf8 = false; while (src < end) { if (!string_examine_chars[(uint8_t)*src]) { src++; } else if (*src == '"') { /* string end */ *hasesc_p = hasesc; *str_end_p = src; *nlines_p = lines; return true; } else if (*src == '\\') { hasesc = true; src++; if (src < end && (*src == '\\' || *src == '"')) src++; } else if (*src & 0x80) { n = utf8_validate_seq(src, end); if (n) { src += n; } else if (check_utf8) { goto badutf; } else { src++; } } else if (*src == '\n') { lines++; src++; } else { goto badutf; } } return err_false(ctx, "Unexpected end of string"); badutf: return err_false(ctx, "Invalid UTF8 sequence"); } /* string boundaries are known, copy and unescape */ static char *process_escapes(struct JsonContext *ctx, const char *src, const char *end, char *dst, char *dstend) { const char *esc; /* process escapes */ while (src < end) { esc = memchr(src, '\\', end - src); if (!esc) { dst = plain_copy(dst, src, end); break; } dst = plain_copy(dst, src, esc); src = esc + 1; switch (*src++) { case '"': *dst++ = '"'; break; case '\\': *dst++ = '\\'; break; case '/': *dst++ = '/'; break; case 'b': *dst++ = '\b'; break; case 'f': *dst++ = '\f'; break; case 'n': *dst++ = '\n'; break; case 'r': *dst++ = '\r'; break; case 't': *dst++ = '\t'; break; case 'u': if (!parse_uescape(ctx, &dst, dstend, &src, end)) return NULL; break; default: return err_null(ctx, "Invalid escape code"); } } return dst; } /* 2-phase string processing */ static bool parse_string(struct JsonContext *ctx, const char **src_p, const char *end) { const char *start, *strend = NULL; bool hasesc = false; char *dst, *dstend; size_t len; struct JsonValue *jv; int64_t lines = 0; /* find string boundaries, validate */ start = *src_p; if (!scan_string(ctx, start, end, &strend, &hasesc, &lines)) return false; /* create value struct */ len = strend - start; jv = mk_value(ctx, JSON_STRING, len + 1, true); if (!jv) return false; dst = get_cstring(jv); dstend = dst + len; /* copy & process escapes */ if (hasesc) { dst = process_escapes(ctx, start, strend, dst, dstend); if (!dst) return false; } else { dst = plain_copy(dst, start, strend); } *dst = '\0'; jv->u.v_size = dst - get_cstring(jv); ctx->linenr += lines; *src_p = strend + 1; return true; } /* * Helpers for relaxed parsing */ static bool skip_comment(struct JsonContext *ctx, const char **src_p, const char *end) { const char *s, *start; char c; size_t lnr; s = start = *src_p; if (s >= end) return false; c = *s++; if (c == '/') { s = memchr(s, '\n', end - s); if (s) { ctx->linenr++; *src_p = s + 1; } else { *src_p = end; } return true; } else if (c == '*') { for (lnr = 0; s + 2 <= end; s++) { if (s[0] == '*' && s[1] == '/') { ctx->linenr += lnr; *src_p = s + 2; return true; } else if (s[0] == '\n') { lnr++; } } } return false; } static bool skip_extra_comma(struct JsonContext *ctx, const char **src_p, const char *end, enum ParseState state) { bool skip = false; const char *src = *src_p; while (src < end && isspace(*src)) { if (*src == '\n') ctx->linenr++; src++; } if (src < end) { if (*src == '}') { if (state == S_DICT_COMMA_OR_CLOSE || state == S_DICT_KEY_OR_CLOSE) skip = true; } else if (*src == ']') { if (state == S_LIST_COMMA_OR_CLOSE || state == S_LIST_VALUE_OR_CLOSE) skip = true; } } *src_p = src; return skip; } /* * Main parser */ /* oldstate + token -> newstate */ static const unsigned char STATE_STEPS[MAX_STATES][MAX_TOKENS] = { [S_INITIAL_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_DONE, [T_OTHER] = S_DONE }, [S_LIST_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_LIST_COMMA_OR_CLOSE, [T_OTHER] = S_LIST_COMMA_OR_CLOSE }, [S_LIST_VALUE_OR_CLOSE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_LIST_COMMA_OR_CLOSE, [T_OTHER] = S_LIST_COMMA_OR_CLOSE, [T_CLOSE_LIST] = S_PARENT }, [S_LIST_COMMA_OR_CLOSE] = { [T_COMMA] = S_LIST_VALUE, [T_CLOSE_LIST] = S_PARENT }, [S_DICT_KEY] = { [T_STRING] = S_DICT_COLON }, [S_DICT_KEY_OR_CLOSE] = { [T_STRING] = S_DICT_COLON, [T_CLOSE_DICT] = S_PARENT }, [S_DICT_COLON] = { [T_COLON] = S_DICT_VALUE }, [S_DICT_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_DICT_COMMA_OR_CLOSE, [T_OTHER] = S_DICT_COMMA_OR_CLOSE }, [S_DICT_COMMA_OR_CLOSE] = { [T_COMMA] = S_DICT_KEY, [T_CLOSE_DICT] = S_PARENT }, }; #define MAPSTATE(state, tok) do { \ int newstate = STATE_STEPS[state][tok]; \ if (!newstate) \ return err_false(ctx, "Unexpected symbol: '%c'", c); \ state = newstate; \ } while (0) /* actual parser */ static bool parse_tokens(struct JsonContext *ctx, const char *src, const char *end) { char c; enum ParseState state = S_INITIAL_VALUE; bool relaxed = ctx->options & JSON_PARSE_RELAXED; while (src < end) { c = *src++; switch (c) { case '\n': ctx->linenr++; case ' ': case '\t': case '\r': case '\f': case '\v': /* common case - many spaces */ while (src < end && *src == ' ') src++; break; case '"': MAPSTATE(state, T_STRING); if (!parse_string(ctx, &src, end)) goto failed; break; case 'n': MAPSTATE(state, T_OTHER); src--; if (!parse_char4(ctx, &src, end, C_NULL, JSON_NULL, 0)) goto failed; continue; case 't': MAPSTATE(state, T_OTHER); src--; if (!parse_char4(ctx, &src, end, C_TRUE, JSON_BOOL, 1)) goto failed; break; case 'f': MAPSTATE(state, T_OTHER); if (!parse_char4(ctx, &src, end, C_ALSE, JSON_BOOL, 0)) goto failed; break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': MAPSTATE(state, T_OTHER); src--; if (!parse_number(ctx, &src, end)) goto failed; break; case '[': MAPSTATE(state, T_OPEN_LIST); if (!open_container(ctx, JSON_LIST, LIST_EXTRA)) goto failed; break; case '{': MAPSTATE(state, T_OPEN_DICT); if (!open_container(ctx, JSON_DICT, DICT_EXTRA)) goto failed; break; case ']': MAPSTATE(state, T_CLOSE_LIST); state = close_container(ctx, state); if (!state) goto failed; break; case '}': MAPSTATE(state, T_CLOSE_DICT); state = close_container(ctx, state); if (!state) goto failed; break; case ':': MAPSTATE(state, T_COLON); if (!real_dict_add_key(ctx, ctx->parent, ctx->cur_key)) goto failed; break; case ',': if (relaxed && skip_extra_comma(ctx, &src, end, state)) continue; MAPSTATE(state, T_COMMA); break; case '/': if (relaxed && skip_comment(ctx, &src, end)) continue; default: return err_false(ctx, "Invalid symbol: '%c'", c); } } if (state != S_DONE) return err_false(ctx, "Container still open"); return true; failed: return false; } /* parser public api */ struct JsonValue *json_parse(struct JsonContext *ctx, const char *json, size_t len) { const char *end = json + len; /* reset parser */ ctx->linenr = 1; ctx->parent = NULL; ctx->cur_key = NULL; ctx->lasterr = NULL; ctx->top = NULL; if (!parse_tokens(ctx, json, end)) return NULL; return ctx->top; } /* * Render value as JSON string. */ static bool render_null(struct RenderState *rs, struct JsonValue *jv) { return mbuf_write(rs->dst, "null", 4); } static bool render_bool(struct RenderState *rs, struct JsonValue *jv) { if (jv->u.v_bool) return mbuf_write(rs->dst, "true", 4); return mbuf_write(rs->dst, "false", 5); } static bool render_int(struct RenderState *rs, struct JsonValue *jv) { char buf[NUMBER_BUF]; int len; len = snprintf(buf, sizeof(buf), "%" PRIi64, jv->u.v_int); if (len < 0 || len >= NUMBER_BUF) return false; return mbuf_write(rs->dst, buf, len); } static bool render_float(struct RenderState *rs, struct JsonValue *jv) { char buf[NUMBER_BUF + 2]; int len; len = dtostr_dot(buf, NUMBER_BUF, jv->u.v_float); if (len < 0 || len >= NUMBER_BUF) return false; if (!memchr(buf, '.', len) && !memchr(buf, 'e', len)) { buf[len++] = '.'; buf[len++] = '0'; } return mbuf_write(rs->dst, buf, len); } static bool escape_char(struct MBuf *dst, unsigned int c) { char ec; char buf[10]; /* start escape */ if (!mbuf_write_byte(dst, '\\')) return false; /* escape same char */ if (c == '"' || c == '\\') return mbuf_write_byte(dst, c); /* low-ascii mess */ switch (c) { case '\b': ec = 'b'; break; case '\f': ec = 'f'; break; case '\n': ec = 'n'; break; case '\r': ec = 'r'; break; case '\t': ec = 't'; break; default: snprintf(buf, sizeof(buf), "u%04x", c); return mbuf_write(dst, buf, 5); } return mbuf_write_byte(dst, ec); } static bool render_string(struct RenderState *rs, struct JsonValue *jv) { const char *s, *last; const char *val = get_cstring(jv); size_t len = jv->u.v_size; const char *end = val + len; unsigned int c; /* start quote */ if (!mbuf_write_byte(rs->dst, '"')) return false; for (s = last = val; s < end; s++) { if (*s == '"' || *s == '\\' || (unsigned char)*s < 0x20 || /* Valid in JSON, but not in JS: \u2028 - Line separator \u2029 - Paragraph separator */ ((unsigned char)s[0] == 0xE2 && (unsigned char)s[1] == 0x80 && ((unsigned char)s[2] == 0xA8 || (unsigned char)s[2] == 0xA9))) { /* flush */ if (last < s) { if (!mbuf_write(rs->dst, last, s - last)) return false; } if ((unsigned char)s[0] == 0xE2) { c = 0x2028 + ((unsigned char)s[2] - 0xA8); last = s + 3; } else { c = (unsigned char)*s; last = s + 1; } /* output escaped char */ if (!escape_char(rs->dst, c)) return false; } } /* flush */ if (last < s) { if (!mbuf_write(rs->dst, last, s - last)) return false; } /* final quote */ if (!mbuf_write_byte(rs->dst, '"')) return false; return true; } /* * Render complex values */ struct ElemWriterState { struct RenderState *rs; char sep; }; static bool list_elem_writer(void *arg, struct JsonValue *elem) { struct ElemWriterState *state = arg; if (state->sep && !mbuf_write_byte(state->rs->dst, state->sep)) return false; state->sep = ','; return render_any(state->rs, elem); } static bool render_list(struct RenderState *rs, struct JsonValue *list) { struct ElemWriterState state; state.rs = rs; state.sep = 0; if (!mbuf_write_byte(rs->dst, '[')) return false; if (!json_list_iter(list, list_elem_writer, &state)) return false; if (!mbuf_write_byte(rs->dst, ']')) return false; return true; } static bool dict_elem_writer(void *ctx, struct JsonValue *key, struct JsonValue *val) { struct ElemWriterState *state = ctx; if (state->sep && !mbuf_write_byte(state->rs->dst, state->sep)) return false; state->sep = ','; if (!render_any(state->rs, key)) return false; if (!mbuf_write_byte(state->rs->dst, ':')) return false; return render_any(state->rs, val); } static bool render_dict(struct RenderState *rs, struct JsonValue *dict) { struct ElemWriterState state; state.rs = rs; state.sep = 0; if (!mbuf_write_byte(rs->dst, '{')) return false; if (!json_dict_iter(dict, dict_elem_writer, &state)) return false; if (!mbuf_write_byte(rs->dst, '}')) return false; return true; } static bool render_invalid(struct RenderState *rs, struct JsonValue *jv) { return false; } /* * Public api */ static bool render_any(struct RenderState *rs, struct JsonValue *jv) { static const render_func_t rfunc_map[] = { render_invalid, render_null, render_bool, render_int, render_float, render_string, render_list, render_dict, }; return rfunc_map[get_type(jv)](rs, jv); } bool json_render(struct MBuf *dst, struct JsonValue *jv) { struct RenderState rs; rs.dst = dst; rs.options = 0; return render_any(&rs, jv); } /* * Examine single value */ enum JsonValueType json_value_type(struct JsonValue *jv) { return get_type(jv); } size_t json_value_size(struct JsonValue *jv) { if (has_type(jv, JSON_STRING) || has_type(jv, JSON_LIST) || has_type(jv, JSON_DICT)) return jv->u.v_size; return 0; } bool json_value_as_bool(struct JsonValue *jv, bool *dst_p) { if (!has_type(jv, JSON_BOOL)) return false; *dst_p = jv->u.v_bool; return true; } bool json_value_as_int(struct JsonValue *jv, int64_t *dst_p) { if (!has_type(jv, JSON_INT)) return false; *dst_p = jv->u.v_int; return true; } bool json_value_as_float(struct JsonValue *jv, double *dst_p) { if (!has_type(jv, JSON_FLOAT)) { if (has_type(jv, JSON_INT)) { *dst_p = jv->u.v_int; return true; } return false; } *dst_p = jv->u.v_float; return true; } bool json_value_as_string(struct JsonValue *jv, const char **dst_p, size_t *size_p) { if (!has_type(jv, JSON_STRING)) return false; *dst_p = get_cstring(jv); if (size_p) *size_p = jv->u.v_size; return true; } /* * Load value from dict. */ static int dict_getter(struct JsonValue *dict, const char *key, unsigned int klen, struct JsonValue **val_p, enum JsonValueType req_type, bool req_value) { struct JsonValue *val, *kjv; struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return false; kjv = cbtree_lookup(tree, key, klen); if (!kjv) { if (req_value) return false; *val_p = NULL; return true; } val = get_next(kjv); if (!req_value && json_value_is_null(val)) { *val_p = NULL; return true; } if (!has_type(val, req_type)) return false; *val_p = val; return true; } bool json_dict_get_value(struct JsonValue *dict, const char *key, struct JsonValue **val_p) { struct CBTree *tree; struct JsonValue *kjv; size_t klen; tree = get_dict_tree(dict); if (!tree) return false; klen = strlen(key); kjv = cbtree_lookup(tree, key, klen); if (!kjv) return false; *val_p = get_next(kjv); return true; } bool json_dict_is_null(struct JsonValue *dict, const char *key) { struct JsonValue *val; if (!json_dict_get_value(dict, key, &val)) return true; return has_type(val, JSON_NULL); } bool json_dict_get_bool(struct JsonValue *dict, const char *key, bool *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_BOOL, true)) return false; return json_value_as_bool(val, dst_p); } bool json_dict_get_int(struct JsonValue *dict, const char *key, int64_t *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_INT, true)) return false; return json_value_as_int(val, dst_p); } bool json_dict_get_float(struct JsonValue *dict, const char *key, double *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_FLOAT, true)) return false; return json_value_as_float(val, dst_p); } bool json_dict_get_string(struct JsonValue *dict, const char *key, const char **dst_p, size_t *len_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_STRING, true)) return false; return json_value_as_string(val, dst_p, len_p); } bool json_dict_get_list(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { return dict_getter(dict, key, strlen(key), dst_p, JSON_LIST, true); } bool json_dict_get_dict(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { return dict_getter(dict, key, strlen(key), dst_p, JSON_DICT, true); } /* * Load optional dict element. */ bool json_dict_get_opt_bool(struct JsonValue *dict, const char *key, bool *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_BOOL, false)) return false; return !val || json_value_as_bool(val, dst_p); } bool json_dict_get_opt_int(struct JsonValue *dict, const char *key, int64_t *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_INT, false)) return false; return !val || json_value_as_int(val, dst_p); } bool json_dict_get_opt_float(struct JsonValue *dict, const char *key, double *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_FLOAT, false)) return false; return !val || json_value_as_float(val, dst_p); } bool json_dict_get_opt_string(struct JsonValue *dict, const char *key, const char **dst_p, size_t *len_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_STRING, false)) return false; return !val || json_value_as_string(val, dst_p, len_p); } bool json_dict_get_opt_list(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_LIST, false)) return false; if (val) *dst_p = val; return true; } bool json_dict_get_opt_dict(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_DICT, false)) return false; if (val) *dst_p = val; return true; } /* * Load value from list. */ bool json_list_get_value(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *val; struct ValueList *vlist; size_t i; vlist = get_list_vlist(list); if (!vlist) return false; if (index >= list->u.v_size) return false; if (!vlist->array && list->u.v_size > 10) prepare_array(list); /* direct fetch */ if (vlist->array) { *val_p = vlist->array[index]; return true; } /* walk */ val = vlist->first; for (i = 0; val; i++) { if (i == index) { *val_p = val; return true; } val = get_next(val); } return false; } bool json_list_is_null(struct JsonValue *list, size_t n) { struct JsonValue *jv; if (!json_list_get_value(list, n, &jv)) return true; return has_type(jv, JSON_NULL); } bool json_list_get_bool(struct JsonValue *list, size_t index, bool *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_bool(jv, val_p); } bool json_list_get_int(struct JsonValue *list, size_t index, int64_t *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_int(jv, val_p); } bool json_list_get_float(struct JsonValue *list, size_t index, double *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_float(jv, val_p); } bool json_list_get_string(struct JsonValue *list, size_t index, const char **val_p, size_t *len_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_string(jv, val_p, len_p); } bool json_list_get_list(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; if (!has_type(jv, JSON_LIST)) return false; *val_p = jv; return true; } bool json_list_get_dict(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; if (!has_type(jv, JSON_DICT)) return false; *val_p = jv; return true; } /* * Iterate over list and dict values. */ struct DictIterState { json_dict_iter_callback_f cb_func; void *cb_arg; }; static bool dict_iter_helper(void *arg, void *jv) { struct DictIterState *state = arg; struct JsonValue *key = jv; struct JsonValue *val = get_next(key); return state->cb_func(state->cb_arg, key, val); } bool json_dict_iter(struct JsonValue *dict, json_dict_iter_callback_f cb_func, void *cb_arg) { struct DictIterState state; struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return false; state.cb_func = cb_func; state.cb_arg = cb_arg; return cbtree_walk(tree, dict_iter_helper, &state); } bool json_list_iter(struct JsonValue *list, json_list_iter_callback_f cb_func, void *cb_arg) { struct JsonValue *elem; struct ValueList *vlist; vlist = get_list_vlist(list); if (!vlist) return false; for (elem = vlist->first; elem; elem = get_next(elem)) { if (!cb_func(cb_arg, elem)) return false; } return true; } /* * Create new values. */ struct JsonValue *json_new_null(struct JsonContext *ctx) { return mk_value(ctx, JSON_NULL, 0, false); } struct JsonValue *json_new_bool(struct JsonContext *ctx, bool val) { struct JsonValue *jv; jv = mk_value(ctx, JSON_BOOL, 0, false); if (jv) jv->u.v_bool = val; return jv; } struct JsonValue *json_new_int(struct JsonContext *ctx, int64_t val) { struct JsonValue *jv; if (val < JSON_MININT || val > JSON_MAXINT) { errno = ERANGE; return NULL; } jv = mk_value(ctx, JSON_INT, 0, false); if (jv) jv->u.v_int = val; return jv; } struct JsonValue *json_new_float(struct JsonContext *ctx, double val) { struct JsonValue *jv; /* check if value survives JSON roundtrip */ if (!isfinite(val)) return false; jv = mk_value(ctx, JSON_FLOAT, 0, false); if (jv) jv->u.v_float = val; return jv; } struct JsonValue *json_new_string(struct JsonContext *ctx, const char *val) { struct JsonValue *jv; size_t len; len = strlen(val); if (!utf8_validate_string(val, val + len)) return NULL; jv = mk_value(ctx, JSON_STRING, len + 1, false); if (jv) { memcpy(get_cstring(jv), val, len + 1); jv->u.v_size = len; } return jv; } struct JsonValue *json_new_list(struct JsonContext *ctx) { return mk_value(ctx, JSON_LIST, LIST_EXTRA, false); } struct JsonValue *json_new_dict(struct JsonContext *ctx) { return mk_value(ctx, JSON_DICT, DICT_EXTRA, false); } /* * Add to containers */ bool json_list_append(struct JsonValue *list, struct JsonValue *val) { if (!val) return false; if (!has_type(list, JSON_LIST)) return false; if (!is_unattached(val)) return false; set_parent(val, list); set_next(val, NULL); real_list_append(list, val); return true; } bool json_list_append_null(struct JsonValue *list) { struct JsonValue *v; v = json_new_null(get_context(list)); return json_list_append(list, v); } bool json_list_append_bool(struct JsonValue *list, bool val) { struct JsonValue *v; v = json_new_bool(get_context(list), val); return json_list_append(list, v); } bool json_list_append_int(struct JsonValue *list, int64_t val) { struct JsonValue *v; v = json_new_int(get_context(list), val); return json_list_append(list, v); } bool json_list_append_float(struct JsonValue *list, double val) { struct JsonValue *v; v = json_new_float(get_context(list), val); return json_list_append(list, v); } bool json_list_append_string(struct JsonValue *list, const char *val) { struct JsonValue *v; v = json_new_string(get_context(list), val); return json_list_append(list, v); } bool json_dict_put(struct JsonValue *dict, const char *key, struct JsonValue *val) { struct JsonValue *kjv; struct JsonContainer *c; if (!key || !val) return false; if (!has_type(dict, JSON_DICT)) return false; if (!is_unattached(val)) return false; c = get_container(dict); kjv = json_new_string(c->c_ctx, key); if (!kjv) return false; if (!real_dict_add_key(c->c_ctx, dict, kjv)) return false; set_next(kjv, val); set_next(val, NULL); set_parent(val, dict); return true; } bool json_dict_put_null(struct JsonValue *dict, const char *key) { struct JsonValue *v; v = json_new_null(get_context(dict)); return json_dict_put(dict, key, v); } bool json_dict_put_bool(struct JsonValue *dict, const char *key, bool val) { struct JsonValue *v; v = json_new_bool(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_int(struct JsonValue *dict, const char *key, int64_t val) { struct JsonValue *v; v = json_new_int(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_float(struct JsonValue *dict, const char *key, double val) { struct JsonValue *v; v = json_new_float(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_string(struct JsonValue *dict, const char *key, const char *val) { struct JsonValue *v; v = json_new_string(get_context(dict), val); return json_dict_put(dict, key, v); } /* * Main context management */ struct JsonContext *json_new_context(const void *cx, size_t initial_mem) { struct JsonContext *ctx; CxMem *pool; pool = cx_new_pool(cx, initial_mem, 8); if (!pool) return NULL; ctx = cx_alloc0(pool, sizeof(*ctx)); if (!ctx) { cx_destroy(pool); return NULL; } ctx->pool = pool; return ctx; } void json_free_context(struct JsonContext *ctx) { if (ctx) { CxMem *pool = ctx->pool; memset(ctx, 0, sizeof(*ctx)); cx_destroy(pool); } } const char *json_strerror(struct JsonContext *ctx) { return ctx->lasterr; } void json_set_options(struct JsonContext *ctx, unsigned int options) { ctx->options = options; } pgqd/lib/usual/pgutil.c0000664000401600040160000001141713175113172013427 0ustar cbecbe/* * Some utility functions for Postgres. * * - Literal & ident quoting. * - Array parsing */ #include #include /* str -> E'str' */ bool pg_quote_literal(char *_dst, const char *_src, int dstlen) { char *dst = _dst; char *end = _dst + dstlen - 2; const char *src = _src; bool stdquote = true; if (dstlen < 3) return false; if (_src == NULL) { if (dstlen < 5) return false; memcpy(_dst, "NULL", 5); return true; } retry: *dst++ = '\''; while (*src && dst < end) { if (*src == '\'') *dst++ = '\''; else if (*src == '\\') { if (stdquote) goto retry_ext; *dst++ = '\\'; } *dst++ = *src++; } if (*src || dst > end) return false; *dst++ = '\''; *dst = 0; return true; retry_ext: /* string contains '\\', retry as E'' string */ dst = _dst; src = _src; *dst++ = 'E'; stdquote = false; goto retry; } static inline bool id_start(unsigned char c) { return (c >= 'a' && c <= 'z') || c == '_'; } static inline bool id_body(unsigned char c) { return id_start(c) || (c >= '0' && c <= '9'); } /* ident -> "ident" */ bool pg_quote_ident(char *_dst, const char *_src, int dstlen) { char *dst = _dst; char *end = _dst + dstlen - 1; const char *src = _src; if (dstlen < 1) return false; if (!id_start(*src)) goto needs_quoting; while (*src && dst < end) { if (!id_body(*src)) goto needs_quoting; *dst++ = *src++; } if (*src) return false; *dst = 0; if (!pg_is_reserved_word(_dst)) return true; needs_quoting: dst = _dst; src = _src; end = _dst + dstlen - 2; if (dstlen < 3) return false; *dst++ = '"'; while (*src && dst < end) { if (*src == '"') *dst++ = *src; *dst++ = *src++; } if (*src) return false; *dst++ = '"'; *dst = 0; return true; } /* schema.name -> "schema"."name" */ bool pg_quote_fqident(char *_dst, const char *_src, int dstlen) { const char *dot = strchr(_src, '.'); char scmbuf[128]; const char *scm; int scmlen; if (dot) { scmlen = dot - _src; if (scmlen >= (int)sizeof(scmbuf)) return false; memcpy(scmbuf, _src, scmlen); scmbuf[scmlen] = 0; scm = scmbuf; _src = dot + 1; } else { scm = "public"; } if (!pg_quote_ident(_dst, scm, dstlen)) return false; scmlen = strlen(_dst); _dst[scmlen] = '.'; _dst += scmlen + 1; dstlen -= scmlen + 1; if (!pg_quote_ident(_dst, _src, dstlen)) return false; return true; } /* * pgarray parsing */ static bool parse_value(struct StrList *arr, const char *val, const char *vend, CxMem *cx) { int len; const char *s; char *str, *p; unsigned c; while (val < vend && isspace(*val)) val++; while (vend > val && isspace(vend[-1])) vend--; if (val == vend) return false; s = val; len = vend - val; if (len == 4 && !strncasecmp(val, "null", len)) { return strlist_append_ref(arr, NULL); } p = str = cx_alloc(cx, len + 1); if (!str) return false; /* unquote & copy */ while (s < vend) { c = *s++; if (c == '"') { while (1) { c = *s++; if (c == '"') break; else if (c == '\\') *p++ = *s++; else *p++ = c; } } else if (c == '\\') { *p++ = *s++; } else *p++ = c; } *p++ = 0; if (!strlist_append_ref(arr, str)) { cx_free(cx, str); return false; } return true; } struct StrList *pg_parse_array(const char *pgarr, CxMem *cx) { const char *s = pgarr; struct StrList *lst; const char *val = NULL; unsigned c; /* skip dimension def "[x,y]={..}" */ if (*s == '[') { s = strchr(s, ']'); if (!s || s[1] != '=') return NULL; s += 2; } if (*s++ != '{') return NULL; lst = strlist_new(cx); if (!lst) return NULL; while (*s) { /* array end */ if (s[0] == '}') { if (s[1] != 0) { goto failed; } if (val) { if (!parse_value(lst, val, s, cx)) goto failed; } return lst; } /* cannot init earlier to support empty arrays */ if (!val) val = s; /* val done? */ if (*s == ',') { if (!parse_value(lst, val, s, cx)) goto failed; val = ++s; continue; } /* scan value */ c = *s++; if (c == '"') { while (1) { c = *s++; if (c == '"') break; else if (c == '\\') { if (!*s) goto failed; s++; } else if (!*s) goto failed; } } else if (c == '\\') { if (!*s) goto failed; s++; } } if (s[-1] != '}') goto failed; return lst; failed: strlist_free(lst); return NULL; } /* * Postgres keyword lookup. */ /* gperf tries ot inline a non-static function. */ #undef inline #undef __inline #undef __attribute__ #define inline #define __inline #define __attribute__(x) #define long uintptr_t /* include gperf code */ const char *pg_keyword_lookup_real(const char *str, unsigned int len); #include bool pg_is_reserved_word(const char *str) { const char *kw = pg_keyword_lookup_real(str, strlen(str)); return kw != NULL; } pgqd/lib/usual/dlfcn.c0000664000401600040160000000240613175113172013207 0ustar cbecbe/* * Dynamic library loading. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef _WIN32 #include /* * win32: Minimal dlopen, dlsym, dlclose, dlerror compat. */ void *dlopen(const char *fn, int flag) { HMODULE h = LoadLibraryEx(fn, NULL, 0); return h; } void *dlsym(void *hptr, const char *fname) { HMODULE h = hptr; FARPROC f = GetProcAddress(h, fname); return f; } int dlclose(void *hptr) { HMODULE h = hptr; return FreeLibrary(h) ? 0 : -1; } const char *dlerror(void) { return strerror(GetLastError()); } #endif pgqd/lib/usual/psrandom.h0000664000401600040160000000347513175113172013760 0ustar cbecbe/* * Pseudo-random bytes. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * Pseudo-random number generator for non-cryptographic purposes. * * By default it's seeded with csrandom so returns unpredictable * values (but not cryptographically stong). But when * pseudo_random_seed() is used, all following values * are determined completely by seed. */ #ifndef _USUAL_PSRANDOM_H_ #define _USUAL_PSRANDOM_H_ #include /** * Return value with uniform probability over whole 32-bit range. */ uint32_t pseudo_random(void); /** * Return with with uniform probability over specific range. */ uint32_t pseudo_random_range(uint32_t upper_bound); /** * Fill buffer with random bytes. */ void pseudo_random_bytes(void *dst, size_t count); /** * Set 128-bit seed. Following values will be * fully deterministic based on seed. */ void pseudo_random_seed(uint64_t a, uint64_t b); /* 128-bit state. Period: 2**128 - 1 */ uint64_t xorshift128plus(uint64_t *s0, uint64_t *s1); /* 1024-bit state. Period: 2**1024 - 1 */ uint64_t xorshift1024plus(uint64_t state[16], unsigned int counter); #endif pgqd/lib/usual/bytemap.h0000664000401600040160000000637413175113172013577 0ustar cbecbe/* * byte map * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Map 256 byte values to bit or int. */ #ifndef _USUAL_BYTEMAP_H_ #define _USUAL_BYTEMAP_H_ #define BITMAP256_SHIFT 5 #define BITMAP256_MASK ((1 << BITMAP256_SHIFT) - 1) /** * Bitmap of 256 bits. */ struct Bitmap256 { uint32_t bmap[256 / 32]; }; /** * Clear bitmap. */ static inline void bitmap256_init(struct Bitmap256 *bmap) { memset(bmap, 0, sizeof(*bmap)); } /** * Set one bit. */ static inline void bitmap256_set(struct Bitmap256 *bmap, uint8_t byte) { bmap->bmap[byte >> BITMAP256_SHIFT] |= 1 << (byte & BITMAP256_MASK); } /** * Check if bit is set. */ static inline bool bitmap256_is_set(const struct Bitmap256 *bmap, uint8_t byte) { return bmap->bmap[byte >> BITMAP256_SHIFT] & (1 << (byte & BITMAP256_MASK)); } /* * Declare const value of bytemap */ /** * Use C preprocessor to fill Bitmap256. * * Usage: * @code * #define check_isdigit(c) ((c) >= '0' && (c) <= '9') * static const struct Bitmap256 map_isdigit = BITMAP256_CONST(check_isdigit); * @endcode */ #define BITMAP256_CONST(check) {{ \ _BMAP256_V32(check,0), _BMAP256_V32(check,32), _BMAP256_V32(check,64), _BMAP256_V32(check,96), \ _BMAP256_V32(check,128), _BMAP256_V32(check,160), _BMAP256_V32(check,192), _BMAP256_V32(check,224) }} #define _BMAP256_V32(ck,p) \ _BMAP256_V8(ck,(p)+0) | _BMAP256_V8(ck,(p)+8) | _BMAP256_V8(ck,(p)+16) | _BMAP256_V8(ck,(p)+24) #define _BMAP256_V8(ck,p) \ _BMAP256_BIT(ck,(p)+0) | _BMAP256_BIT(ck,(p)+1) | _BMAP256_BIT(ck,(p)+2) | _BMAP256_BIT(ck,(p)+3) | \ _BMAP256_BIT(ck,(p)+4) | _BMAP256_BIT(ck,(p)+5) | _BMAP256_BIT(ck,(p)+6) | _BMAP256_BIT(ck,(p)+7) #define _BMAP256_BIT(ck,p) (ck(p) ? (1 << ((p) & BMAP256_MASK)) : 0) /** * Use C preprocessor to generate array of 256 values. * * Usage: * @code * #define my_hexval(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : ( \ * ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : ( \ * ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : -1 ))) * static const int map_hexval[] = INTMAP256_CONST(my_hexval); * @endcode */ #define INTMAP256_CONST(map_value) { _INTMAP_V128(map_value,0), _INTMAP_V128(map_value,128) } #define _INTMAP_V128(mf,n) _INTMAP_V32(mf,(n)+0*32), _INTMAP_V32(mf,(n)+1*32), _INTMAP_V32(mf,(n)+2*32), _INTMAP_V32(mf,(n)+3*32) #define _INTMAP_V32(mf,n) _INTMAP_V8(mf,(n)+0*8), _INTMAP_V8(mf,(n)+1*8), _INTMAP_V8(mf,(n)+2*8), _INTMAP_V8(mf,(n)+3*8) #define _INTMAP_V8(mf,n) mf((n)+0), mf((n)+1), mf((n)+2), mf((n)+3), mf((n)+4), mf((n)+5), mf((n)+6), mf((n)+7) #endif pgqd/lib/usual/netdb.h0000664000401600040160000000363013175113172013222 0ustar cbecbe/* * libusual - Utility library for C * * Copyright (c) 2010 Marko Kreen, Skype Technologies * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * DNS lookup. */ #ifndef _USUAL_NETDB_H_ #define _USUAL_NETDB_H_ #include #ifdef HAVE_NETDB_H #include #endif #ifndef HAVE_GETADDRINFO_A /** Async execution */ #ifndef GAI_WAIT #define GAI_WAIT 0 #endif /** Synchronous execution */ #ifndef GAI_NOWAIT #define GAI_NOWAIT 1 #endif /* avoid name conflicts */ #define gaicb usual_gaicb #define getaddrinfo_a(a,b,c,d) usual_getaddrinfo_a(a,b,c,d) /** * Request data for getaddrinfo_a(). * * Fields correspond to getaddrinfo() parameters. */ struct gaicb { /** node name */ const char *ar_name; /** service name */ const char *ar_service; /** hints */ const struct addrinfo *ar_request; /** result */ struct addrinfo *ar_result; /* internal state */ int _state; }; #ifndef EAI_INPROGRESS #define EAI_INPROGRESS -100 #endif #ifndef EAI_SYSTEM #define EAI_SYSTEM -10 #endif #define gai_error(gcb) ((gcb)->_state) /** * Compat: Async DNS lookup. */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp); #endif /* HAVE_GETADDRINFO_A */ #endif /* _USUAL_NETDB_H_ */ pgqd/lib/usual/fileutil.c0000664000401600040160000000615213175113172013740 0ustar cbecbe/* * File access utils. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_SYS_MMAN_H #include #endif #include #include #include /* * Load text file into C string. */ void *load_file(const char *fn, size_t *len_p) { struct stat st; char *buf = NULL; int res; FILE *f; res = stat(fn, &st); if (res < 0) return NULL; buf = malloc(st.st_size + 1); if (!buf) return NULL; f = fopen(fn, "r"); if (!f) { free(buf); return NULL; } if ((res = fread(buf, 1, st.st_size, f)) < 0) { free(buf); fclose(f); return NULL; } fclose(f); buf[res] = 0; if (len_p) *len_p = res; return buf; } /* * Read file line-by-line, call user func on each. */ bool foreach_line(const char *fn, procline_cb proc_line, void *arg) { char *ln = NULL; size_t len = 0; ssize_t res; FILE *f = fopen(fn, "rb"); bool ok = false; if (!f) return false; while (1) { res = getline(&ln, &len, f); if (res < 0) { if (feof(f)) ok = true; break; } if (!proc_line(arg, ln, res)) break; } fclose(f); free(ln); return ok; } /* * Find file size. */ ssize_t file_size(const char *fn) { struct stat st; if (stat(fn, &st) < 0) return -1; return st.st_size; } /* * Map a file into mem. */ #ifdef HAVE_MMAP int map_file(struct MappedFile *m, const char *fname, int rw) { struct stat st; m->fd = open(fname, rw ? O_RDWR : O_RDONLY); if (m->fd < 0) return -1; if (fstat(m->fd, &st) < 0) { close(m->fd); return -1; } m->len = st.st_size; m->ptr = mmap(NULL, m->len, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, m->fd, 0); if (m->ptr == MAP_FAILED) { close(m->fd); return -1; } return 0; } void unmap_file(struct MappedFile *m) { munmap(m->ptr, m->len); close(m->fd); m->ptr = NULL; m->fd = 0; } #endif #ifndef HAVE_GETLINE /* * Read line from FILE with dynamic allocation. */ int getline(char **line_p, size_t *size_p, void *_f) { FILE *f = _f; char *p; int len = 0; if (!*line_p || *size_p < 128) { p = realloc(*line_p, 512); if (!p) return -1; *line_p = p; *size_p = 512; } while (1) { p = fgets(*line_p + len, *size_p - len, f); if (!p) return len ? len : -1; len += strlen(p); if ((*line_p)[len - 1] == '\n') return len; p = realloc(*line_p, *size_p * 2); if (!p) return -1; *line_p = p; *size_p *= 2; } } #endif pgqd/lib/usual/logging.h0000664000401600040160000001166413175113172013562 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Logging framework for unix services. * * * Supported outputs: * - syslog * - log file * - stderr * * @section logging_prefix Logging context * * It is possible to pass context info to all logging calls * and later add details to log lines or to filter based on it. * * Each call references 2 macros: * - LOG_CONTEXT_DEF - which can define/call any variables * - LOG_CONTEXT - which should return a pointer variable. * * Later, global callback function \ref logging_prefix_cb * will get this pointer with destination buffer and can either * add more info for log line or tell to skip logging this message. */ #ifndef _USUAL_LOGGING_H_ #define _USUAL_LOGGING_H_ #include /* internal log levels */ enum LogLevel { LG_FATAL = 0, LG_ERROR = 1, LG_WARNING = 2, LG_STATS = 3, LG_INFO = 4, LG_DEBUG = 5, LG_NOISE = 6, }; #ifndef LOG_CONTEXT_DEF /** Example: Prepare dummy context pointer */ #define LOG_CONTEXT_DEF void *_log_ctx = NULL #endif #ifndef LOG_CONTEXT /** Example: Reference dummy context pointer */ #define LOG_CONTEXT _log_ctx #endif /** * Signature for logging_prefix_cb. Return value is either added string length in dst * or negative value to skip logging. */ typedef int (*logging_prefix_fn_t)(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen); /** * Optional global callback for each log line. * * It can either add info to log message or skip logging it. */ extern logging_prefix_fn_t logging_prefix_cb; /** * Global verbosity level. * * 0 - show only info level msgs (default) * 1 - show debug msgs (log_debug) * 2 - show noise msgs (log_noise) */ extern int cf_verbose; /** * Toggle logging to stderr. Default: 1. * daemon.c turns this off if goes to background */ extern int cf_quiet; /** * Logfile location, default NULL */ extern const char *cf_logfile; /** Syslog on/off */ extern int cf_syslog; /** ident for syslog, if NULL syslog is disabled (default) */ extern const char *cf_syslog_ident; /** Facility name */ extern const char *cf_syslog_facility; /** Max log level for syslog writer */ extern enum LogLevel cf_syslog_level; /** Max log level for logfile writer */ extern enum LogLevel cf_logfile_level; /** Max log level for stderr writer */ extern enum LogLevel cf_stderr_level; /* * Internal API. */ /* non-fatal logging */ void log_generic(enum LogLevel level, void *ctx, const char *s, ...) _PRINTF(3, 4); /* this is also defined in base.h for Assert() */ void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *s, ...) _PRINTF(6, 7); /* * Public API */ /** Log error message */ #define log_error(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_ERROR, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log warning message */ #define log_warning(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_WARNING, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log stats (liveness) message */ #define log_stats(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_STATS, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log info message */ #define log_info(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_INFO, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log debug message */ #define log_debug(...) do { LOG_CONTEXT_DEF; \ if (unlikely(cf_verbose > 0)) \ log_generic(LG_DEBUG, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log debug noise */ #define log_noise(...) do { LOG_CONTEXT_DEF; \ if (unlikely(cf_verbose > 1)) \ log_generic(LG_NOISE, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log and die. It also logs source location */ #define fatal(...) do { LOG_CONTEXT_DEF; \ log_fatal(__FILE__, __LINE__, __func__, false, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** Log strerror and die. Error message also includes strerror(errno) */ #define fatal_perror(...) do { LOG_CONTEXT_DEF; \ log_fatal(__FILE__, __LINE__, __func__, true, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** Less verbose fatal() */ #define die(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_FATAL, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** * Close open logfiles and syslog. * * Useful when rotating log files. */ void reset_logging(void); #endif pgqd/lib/usual/base.h0000664000401600040160000002125013175113172013036 0ustar cbecbe/** @file * Basic C environment. */ /* * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_BASE_H_ #define _USUAL_BASE_H_ #ifdef USUAL_TEST_CONFIG #include "test_config.h" #elif defined(_MSC_VER) #include #else #include #endif /* solaris is broken otherwise */ #if defined(__sun) #define _XPG4_2 #define __EXTENSIONS__ #endif /* C11 */ #ifndef __STDC_WANT_LIB_EXT1__ #define __STDC_WANT_LIB_EXT1__ 1 #endif #include #ifdef HAVE_SYS_PARAM_H #include #endif #include #include #ifdef HAVE_INTTYPES_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDBOOL_H #include #else /* we really want bool type */ typedef enum { true=1, false=0 } bool; #endif #ifdef WIN32 #include #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) #else #define DLLEXPORT #define DLLIMPORT #endif #ifndef PRIdZ #define PRIdZ "zd" /**< printf 'd' format specifier for ssize_t */ #define PRIiZ "zi" /**< printf 'i' format specifier for ssize_t */ #define PRIoZ "zo" /**< printf 'o' format specifier for size_t */ #define PRIuZ "zu" /**< printf 'u' format specifier for size_t */ #define PRIxZ "zx" /**< printf 'x' format specifier for size_t */ #define PRIXZ "zX" /**< printf 'X' format specifier for size_t */ #endif /** give offset of a field inside struct */ #ifndef offsetof #define offsetof(type, field) ((unsigned long)&(((type *)0)->field)) #endif /** given pointer to field inside struct, return pointer to struct */ #ifndef container_of #define container_of(ptr, type, field) ((type *)((char *)(ptr) - offsetof(type, field))) #endif /** get alignment requirement for a type */ #ifndef alignof #define alignof(type) offsetof(struct { char c; type t; }, t) #endif /** power-of-2 alignment */ #ifndef CUSTOM_ALIGN #define CUSTOM_ALIGN(x, a) (((uintptr_t)(x) + (uintptr_t)(a) - 1) & ~((uintptr_t)(a) - 1)) #endif /** preferred alignment */ #ifndef ALIGN #define ALIGN(x) CUSTOM_ALIGN(x, sizeof(long)) #endif /** number of elements in array */ #define ARRAY_NELEM(a) (sizeof(a) / sizeof((a)[0])) /** * Compat helper to specify array with unknown length. * * Usage: * * @code * char flex_string[FLEX_ARRAY]; * @endcode */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #define FLEX_ARRAY #elif defined(__GNUC__) && (__GNUC__ >= 3) #define FLEX_ARRAY #else #define FLEX_ARRAY 1 #endif /** Make string token from C expression */ #define STR(x) _STR_(x) #define _STR_(x) #x /** Make single C token from 2 separate tokens */ #define CONCAT(a, b) _CONCAT_(a, b) #define _CONCAT_(a, b) a ## b /** Make single C token from 3 separate tokens */ #define CONCAT3(a, b, c) _CONCAT3_(a, b, c) #define _CONCAT3_(a, b, c) a ## b ## c /** Make single C token from 4 separate tokens */ #define CONCAT4(a, b, c, d) _CONCAT4_(a, b, c, d) #define _CONCAT4_(a, b, c, d) a ## b ## c ## d /** Pre-processor macro for current function name. */ #ifndef HAVE_FUNCNAME__FUNC #define __func__ __FUNCTION__ #endif /** * @name Compiler checks, mainly for internal usage. * * @{ */ /** Pre-processor macro to check if compiler is GCC with high enough version */ #define _COMPILER_GNUC(maj,min) (defined(__GNUC__) && \ ((__GNUC__ > (maj)) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))) /** Pre-processor macro to check if compiler is CLANG with high enough version */ #define _COMPILER_CLANG(maj,min) (defined(__clang__) && \ ((__clang_major__ > (maj)) || (__clang_major__ == (maj) && __clang_minor__ >= (min)))) /** Pre-processor macro to check if compiler is Visual C with high enough version */ #define _COMPILER_MSC(ver) (defined(_MSC_VER) && (_MSC_VER >= (ver))) /** Pre-processor macro to check if compiler is Intel CC with high enough version */ #define _COMPILER_ICC(ver) (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= (ver))) /* * clang compat * * They work only if the compiler is clang, * return 0 otherwise. */ #ifndef __has_builtin #define __has_builtin(x) (0) #endif #ifndef __has_feature #define __has_feature(x) (0) #endif #ifndef __has_extension #define __has_extension(x) __has_feature(x) #endif #ifndef __has_attribute #define __has_attribute(x) (0) #endif /* * clang macros that cannot be defined here: * __is_identifier * __has_include * __has_include_next * __has_warning */ /** * @} * * @name Function/struct attributes. * * @{ */ /** Disable padding for structure */ #ifndef _MSC_VER #define _PACKED __attribute__((packed)) #endif /* * make compiler do something useful */ /** Show warning if function result is not used */ #if _COMPILER_GNUC(4,0) || __has_attribute(warn_unused_result) #define _MUSTCHECK __attribute__((warn_unused_result)) #else #define _MUSTCHECK #endif /** Show warning if used */ #if _COMPILER_GNUC(4,0) || __has_attribute(deprecated) #define _DEPRECATED __attribute__((deprecated)) #else #define _DEPRECATED #endif /** Check printf-style format and arg sanity */ #if _COMPILER_GNUC(4,0) || __has_attribute(printf) #define _PRINTF(fmtpos, argpos) __attribute__((format(printf, fmtpos, argpos))) #else #define _PRINTF(fmtpos, argpos) #endif /** Function returns new pointer */ #if _COMPILER_GNUC(4,0) || __has_attribute(malloc) #define _MALLOC __attribute__((malloc)) #else #define _MALLOC #endif /** Disable 'unused' warning for function/argument. */ #if _COMPILER_GNUC(4,0) || __has_attribute(unused) #define _UNUSED __attribute__((unused)) #else #define _UNUSED #endif /** Do not inline function. */ #if _COMPILER_GNUC(4,0) || __has_attribute(noinline) #define _NOINLINE __attribute__((noinline)) #else #define _NOINLINE #endif /** Indicates that function never returns */ #if _COMPILER_GNUC(4,0) || __has_attribute(noreturn) #define _NORETURN __attribute__((noreturn)) #else #define _NORETURN #endif /** Hint for compiler that expression (x) is likely to be true */ #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_expect) #define likely(x) __builtin_expect(!!(x), 1) #else #define likely(x) (x) #endif /** Hint for compiler that expression (x) is likely to be false */ #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_expect) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define unlikely(x) (x) #endif /* @} */ /** * Compile-time assert. * * Expression must be evaluatable at compile time. * If false, stop compilation with message. * * It can be used in either global or function scope. */ #ifndef static_assert #if _COMPILER_GNUC(4,6) || _COMPILER_MSC(1600) || __has_feature(c_static_assert) /* Version for new compilers */ #define static_assert(expr, msg) _Static_assert(expr, msg) #else /* Version for old compilers */ #define static_assert(expr, msg) enum { CONCAT4(static_assert_failure_, __LINE__, _, __COUNTER__) = 1/(1 != (1 + (expr))) } #endif #endif /* !static_assert */ /** assert() that uses module */ #ifndef Assert #ifdef CASSERT void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *s, ...) _PRINTF(6, 7); #define Assert(e) \ do { \ if (unlikely(!(e))) { \ log_fatal(__FILE__, __LINE__, __func__, false, NULL, \ "Assert(%s) failed", #e); \ abort(); \ } \ } while (0) #else #define Assert(e) #endif #endif /* Fix posix bug by accepting const pointer. */ static inline void _const_free(const void *p) { free((void *)p); } /** Compat: make free() accept const pointer */ #define free(x) _const_free(x) /** Zeroing malloc */ _MUSTCHECK static inline void *zmalloc(size_t len) { return calloc(1, len); } #ifndef HAVE_POSIX_MEMALIGN #define posix_memalign(a,b,c) usual_memalign(a,b,c) /** Compat: posix_memalign() */ int posix_memalign(void **ptr_p, size_t align, size_t len); #endif #ifndef HAVE_REALLOCARRAY #define reallocarray(a,b,c) usual_reallocarray(a,b,c) /** * Same as realloc(), but safely calculates total size. */ void *reallocarray(void *p, size_t count, size_t size); #endif #endif pgqd/lib/usual/wchar.c0000664000401600040160000000652213175113172013230 0ustar cbecbe/* * wchar utility functions. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include wchar_t *mbstr_decode(const char *str, int str_len, int *wlen_p, wchar_t *wbuf, int wbuf_len, bool allow_invalid) { mbstate_t ps; int clen; wchar_t *dst, *w, *wend; const char *s; const char *str_end; int wmax; if (str_len < 0) str_len = strlen(str); str_end = str + str_len; /* max number of wchar_t that the output can take plus zero-terminator */ wmax = str_len + 1; if (wbuf != NULL && wmax < wbuf_len) { dst = wbuf; } else { dst = malloc(sizeof(wchar_t) * wmax); if (!dst) return NULL; } /* try full decode at once */ s = str; memset(&ps, 0, sizeof(ps)); clen = mbsnrtowcs(dst, &s, str_len, wmax, &ps); if (clen >= 0) { if (wlen_p) *wlen_p = clen; dst[clen] = 0; return dst; } if (!allow_invalid) goto fail; /* full decode failed, decode chars one-by-one */ s = str; w = dst; wend = dst + wmax - 1; memset(&ps, 0, sizeof(ps)); while (s < str_end && w < wend) { clen = mbrtowc(w, s, str_end - s, &ps); if (clen > 0) { /* single char */ w++; s += clen; } else if (clen == 0) { /* string end */ break; } else if (allow_invalid) { /* allow invalid encoding */ memset(&ps, 0, sizeof(ps)); *w++ = (unsigned char)*s++; } else { /* invalid encoding */ goto fail; } } /* make sure we got string end */ if (s < str_end && *s != '\0') goto fail; *w = 0; if (wlen_p) *wlen_p = w - dst; return dst; fail: if (dst != wbuf) free(dst); return NULL; } wctype_t wctype_wcsn(const wchar_t *name, unsigned int namelen) { char buf[10]; unsigned int i; if (namelen >= sizeof(buf)) return (wctype_t)0; for (i = 0; i < namelen; i++) { wchar_t c = name[i]; if (c < 0x20 || c > 127) return (wctype_t)0; buf[i] = c; } buf[i] = 0; return wctype(buf); } #ifndef HAVE_MBSNRTOWCS size_t mbsnrtowcs(wchar_t *dst, const char **src_p, size_t srclen, size_t dstlen, mbstate_t *ps) { int clen; const char *s, *s_end; wchar_t *w; mbstate_t pstmp; size_t count = 0; if (!ps) { memset(&pstmp, 0, sizeof(pstmp)); ps = &pstmp; } s = *src_p; s_end = s + srclen; w = dst; while (s < s_end) { if (w && count >= dstlen) { /* dst is full */ break; } clen = mbrtowc(w, s, s_end - s, ps); if (clen > 0) { /* proper character */ if (w) w++; count++; s += clen; } else if (clen < 0) { /* invalid encoding */ *src_p = s; return (size_t)(-1); } else { /* end of string */ if (w) *w = 0; *src_p = NULL; return count; } } /* end due to srclen */ *src_p = s; return count; } #endif pgqd/lib/usual/mempool.c0000664000401600040160000000346313175113172013575 0ustar cbecbe/* * Simple memory pool for variable-length allocations. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * Allows allocation of several variable-sized objects, * freeing them all together. * * ToDo: make it more 'obstack'-like (???) * - free_last * - resize_last * - append */ struct MemPool { struct MemPool *prev; unsigned size; unsigned used; }; void *mempool_alloc(struct MemPool **pool, unsigned size) { struct MemPool *cur = *pool; void *ptr; unsigned nsize; size = ALIGN(size); if (cur && cur->used + size <= cur->size) { ptr = (char *)(cur + 1) + cur->used; cur->used += size; return ptr; } else { nsize = cur ? (2 * cur->size) : 512; while (nsize < size) nsize *= 2; cur = calloc(1, sizeof(*cur) + nsize); if (cur == NULL) return NULL; cur->used = size; cur->size = nsize; cur->prev = *pool; *pool = cur; return (char *)(cur + 1); } } void mempool_destroy(struct MemPool **pool) { struct MemPool *cur, *tmp; if (!pool) return; for (cur = *pool, *pool = NULL; cur; ) { tmp = cur->prev; free(cur); cur = tmp; } } pgqd/lib/usual/fnmatch.h0000664000401600040160000000330513175113172013545 0ustar cbecbe/* * fnmatch.h * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file * Theme include for strings. */ #ifndef _USUAL_FNMATCH_H_ #define _USUAL_FNMATCH_H_ #include #if defined(HAVE_FNMATCH_H) && defined(FNM_CASEFOLD) #include #else /* fnmatch missing or incomplete */ #define NEED_USUAL_FNMATCH #endif #ifdef NEED_USUAL_FNMATCH #define fnmatch(p,s,f) usual_fnmatch(p,s,f) /** Do not allow wildcard to match '/' */ #define FNM_PATHNAME 1 /** Treat '\\' as literal value */ #define FNM_NOESCAPE 2 /** Do not allow wildcard to match leading '.' */ #define FNM_PERIOD 4 /** (GNU) Match case-insensitively */ #define FNM_CASEFOLD 8 /** (GNU) Match leading directory in path */ #define FNM_LEADING_DIR 16 /* (GNU) random alias */ #define FNM_FILE_NAME FNM_PATHNAME /** Returned on no match */ #define FNM_NOMATCH 1 /** * Compat: fnmatch() */ int fnmatch(const char *pat, const char *str, int flags); #endif /* NEED_USUAL_FNMATCH */ #endif /* !_USUAL_FNMATCH_H_ */ pgqd/lib/usual/aatree.h0000664000401600040160000000533513175113172013373 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * AA-Tree - Binary tree with embeddable nodes. * * AA-Tree (Arne Andersson tree) is a simplified Red-Black tree. */ #ifndef _USUAL_AATREE_H_ #define _USUAL_AATREE_H_ #include struct AATree; struct AANode; /** Callback for node comparision against value */ typedef int (*aatree_cmp_f)(uintptr_t, struct AANode *node); /** Callback for walking the tree */ typedef void (*aatree_walker_f)(struct AANode *n, void *arg); /** * Tree header, for storing helper functions. */ struct AATree { struct AANode *root; int count; aatree_cmp_f node_cmp; aatree_walker_f release_cb; }; /** * Tree node. Embeddable, parent structure should be taken * with container_of(). * * Techinally, the full level is not needed and 2-lowest * bits of either ->left or ->right would be enough * to keep track of structure. Currently this is not * done to keep code simple. */ struct AANode { struct AANode *left; /**< smaller values */ struct AANode *right; /**< larger values */ int level; /**< number of black nodes to leaf */ }; /** * Walk order types. */ enum AATreeWalkType { AA_WALK_IN_ORDER = 0, /* left->self->right */ AA_WALK_PRE_ORDER = 1, /* self->left->right */ AA_WALK_POST_ORDER = 2, /* left->right->self */ }; /** Initialize structure */ void aatree_init(struct AATree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb); /** Search for node */ struct AANode *aatree_search(struct AATree *tree, uintptr_t value); /** Insert new node */ void aatree_insert(struct AATree *tree, uintptr_t value, struct AANode *node); /** Remote node */ void aatree_remove(struct AATree *tree, uintptr_t value); /** Walk over all nodes */ void aatree_walk(struct AATree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg); /** Free */ void aatree_destroy(struct AATree *tree); /** Check if terminal node. */ static inline int aatree_is_nil_node(const struct AANode *node) { return (node->left == node); } #endif pgqd/lib/usual/fileutil.h0000664000401600040160000000332113175113172013740 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * File access utils. */ #ifndef _USUAL_FILEUTIL_H_ #define _USUAL_FILEUTIL_H_ #include #include /** Info about mapped file */ struct MappedFile { int fd; unsigned len; void *ptr; }; /** Signature for per-line callback */ typedef bool (*procline_cb)(void *arg, const char *line, ssize_t len); /** Read file into memory */ void *load_file(const char *fn, size_t *len_p); /** Loop over lines in file */ bool foreach_line(const char *fn, procline_cb proc_line, void *arg); /** Get file size */ ssize_t file_size(const char *fn); /** Map file into memory */ int map_file(struct MappedFile *m, const char *fname, int rw) _MUSTCHECK; /** Unmap previously mapped file */ void unmap_file(struct MappedFile *m); #if !defined(HAVE_GETLINE) #define getline(a,b,c) compat_getline(a,b,c) /** * Compat: Read line from file */ int getline(char **line_p, size_t *size_p, void *f); #endif #endif pgqd/lib/usual/talloc.c0000664000401600040160000010173213175113172013401 0ustar cbecbe/* * talloc.c - implementation of "talloc" API from Samba. * * Copyright (c) 2013 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #ifndef HAVE_STRNLEN #include /* needed for compat strnlen prototype */ #endif #include #define MAGIC_USED 0xF100F7 /* allocated block */ #define MAGIC_FREE 0x8600CB /* freed block */ #define MAGIC_MASK 0xFFFFFF /* keep only magic */ #define FLAG_PENDING (1 << 24) /* partially freed */ #define FLAG_USE_MEMLIMIT (1 << 25) /* some parent has memlimit */ #define FLAG_HAS_MEMLIMIT (1 << 26) /* current node has TLimit child */ /* flags parent passes to children */ #define INHERIT_FLAGS (FLAG_USE_MEMLIMIT) /* recursion limit */ #define TALLOC_MAX_DEPTH 10000 /* Don't deal with extreme areas */ #define TALLOC_MAXLEN 0x10000000 /* 256MB */ /* header size that is prepended to each pointer */ #define THSIZE (sizeof(struct THeader)) /* * Prefix on each allocated chunk. * * child_list - internal chunks are put into start of list, * use allocations at the end. freeing happens * from start. this makes sure refs are freed * before other objects. */ struct THeader { uint32_t th_flags; /* flags & magic */ uint32_t size; /* requested size */ CxMem *cx; /* low-level allocation context */ struct THeader *parent; /* parent node, may be NULL */ struct List node; /* node in parent->child_list */ struct List child_list; /* contains child->node */ struct List ref_list; /* contains TRef->ref_node */ const char *name; /* pointer to name string */ talloc_destructor_f destructor; /* function to be called on free */ }; /* * Per-reference struct, attached as child to non-primary parent. */ struct TRef { struct List ref_node; /* node in ->ref_list */ struct TRef *paired_ref; /* track paired helper ref */ }; /* * Track memory limits. Attached as child to * the node limits were set on. FLAG_USE_MEMLIMIT says * if it needs to be checked. */ struct TLimit { ssize_t max_size; ssize_t cur_size; }; /* * Internal helper functions. */ static void log_to_stderr(const char *message); static void do_abort(const char *fmt, ...) _NORETURN; static void do_log(const char *fmt, ...); static void do_dbg(const char *fmt, ...); static int ref_destructor(void *ptr); static bool apply_memlimit(struct THeader *parent, ssize_t delta, bool force); static void move_memlimit(struct THeader *t, struct THeader *newparent, struct THeader *oldparent); static void *find_ptr_from_ref(const struct TRef *ref); /* * Global variables. */ /* log callbacks */ static void (*_log_cb)(const char *message) = log_to_stderr; static void (*_abort_cb)(const char *reason); /* context for parent==NULL */ static void *null_context; /* autofree context */ static void *autofree_ctx; /* names for internal allocations */ static const char MEMLIMIT_NAME[] = ".memlimit"; static const char REF_NAME[] = ".ref"; static const char NULL_NAME[] = ".null-context"; static const char AUTOFREE_NAME[] = ".autofree"; static const char UNNAMED_NAME[] = "UNNAMED"; /* flags to atexit callback */ static int leak_report; static int debug_level; void talloc_set_debug(int level) { debug_level = level; } /* * Internal utils. */ static inline bool has_flags(const struct THeader *t, uint32_t flags) { return (t->th_flags & flags) > 0; } static inline void set_flags(struct THeader *t, uint32_t flags) { t->th_flags |= flags; } static inline void clear_flags(struct THeader *t, uint32_t flags) { t->th_flags &= ~flags; } static inline bool hdr_is_ref(const struct THeader *t) { if (t->destructor == ref_destructor) return true; return false; } static inline void check_magic(const struct THeader *t, const char *pos) { uint32_t magic = t->th_flags & MAGIC_MASK; if (magic != MAGIC_USED) { if (magic == MAGIC_FREE) do_abort("Use after free - %s", pos); else do_abort("Invalid magic - %s", pos); } } static inline void *hdr2ptr(const struct THeader *t) { if (!t) return NULL; check_magic(t, "hdr2ptr"); return (void *)(t + 1); } static inline struct THeader *ptr2hdr(const void *ptr) { struct THeader *t; if (!ptr) return NULL; t = ((struct THeader *)ptr) - 1; check_magic(t, "ptr2hdr"); return t; } static inline size_t total_size(size_t alloc) { return ALIGN(alloc) + THSIZE; } /* if FLAG_CXOWNER is set, this->cx is for children */ static CxMem *get_owner_cx(struct THeader *t) { return t->cx; } /* add refs to start, others to end */ static void add_child(struct THeader *parent, struct THeader *child) { if (parent) { if (hdr_is_ref(child)) list_prepend(&parent->child_list, &child->node); else list_append(&parent->child_list, &child->node); } } /* * actual alloc */ static struct THeader *hdr_alloc_cx(CxMem *cx, struct THeader *parent, size_t len, bool prepend) { struct THeader *t; if (len > TALLOC_MAXLEN) return NULL; if (!parent) parent = ptr2hdr(null_context); if (!apply_memlimit(parent, total_size(len), false)) return NULL; t = cx_alloc(cx, total_size(len)); if (!t) { apply_memlimit(parent, -total_size(len), false); return NULL; } t->th_flags = MAGIC_USED; t->size = len; t->cx = cx; t->parent = parent; list_init(&t->node); list_init(&t->child_list); list_init(&t->ref_list); t->name = NULL; t->destructor = NULL; if (parent) { set_flags(t, parent->th_flags & INHERIT_FLAGS); if (prepend) list_prepend(&parent->child_list, &t->node); else list_append(&parent->child_list, &t->node); } return t; } /* * Allocate */ void *talloc_from_cx(CxMem *cx, size_t len, const char *name) { struct THeader *t; t = hdr_alloc_cx(cx, NULL, len, false); if (!t) return NULL; t->name = name; return hdr2ptr(t); } void *_talloc_const_name(const void *parent, size_t elem_size, size_t count, bool zerofill, const char *name) { struct THeader *t; void *res; size_t size; struct THeader *tparent; CxMem *cx; if (!safe_mul_size(&size, elem_size, count)) return NULL; tparent = ptr2hdr(parent); cx = tparent ? tparent->cx : NULL; t = hdr_alloc_cx(cx, tparent, size, false); if (!t) return NULL; res = hdr2ptr(t); if (zerofill) memset(res, 0, size); t->name = name; return res; } void *_talloc_format_name(const void *parent, size_t elem_size, size_t count, bool zerofill, const char *fmt, ...) { void *res; va_list ap; void *name; res = _talloc_const_name(parent, elem_size, count, zerofill, NULL); if (res) { va_start(ap, fmt); name = talloc_vasprintf(res, fmt, ap); va_end(ap); if (!name) { talloc_free(res); return NULL; } talloc_set_name_const(res, name); } return res; } /* alloc node and put into start of child_list */ static void *internal_alloc_prepend(const void *parent, size_t size, const char *name) { struct THeader *t; void *res; struct THeader *tparent; CxMem *cx; tparent = ptr2hdr(parent); cx = tparent ? tparent->cx : NULL; t = hdr_alloc_cx(cx, tparent, size, true); if (!t) return NULL; res = hdr2ptr(t); t->name = name; return res; } /* * Freeing */ #undef talloc_set_destructor void talloc_set_destructor(const void *ptr, talloc_destructor_f destructor) { struct THeader *t; t = ptr2hdr(ptr); if (t) t->destructor = destructor; } /* attach undying child to live parent */ static void throw_child(struct THeader *t) { struct THeader *parent = t->parent; while (parent && has_flags(parent, FLAG_PENDING)) parent = parent->parent; talloc_reparent(hdr2ptr(t->parent), hdr2ptr(parent), hdr2ptr(t)); } static void free_children(const void *ptr, bool free_name, const char *source_pos) { struct List *el, *tmp; struct THeader *tchild; struct THeader *t; void *child; t = ptr2hdr(ptr); if (!t) return; list_for_each_safe(el, &t->child_list, tmp) { tchild = container_of(el, struct THeader, node); child = hdr2ptr(tchild); if (free_name) { if (child == t->name) t->name = NULL; } else if (child == t->name) { continue; } else if (tchild->name == MEMLIMIT_NAME) { continue; } if (talloc_unlink(ptr, child) != 0) { //do_dbg("DBG: free_children: unlink failed: %s", talloc_get_name(child)); throw_child(tchild); } } } void _talloc_free_children(const void *ptr, const char *source_pos) { free_children(ptr, false, source_pos); } /* what happens when refs are present */ static int free_with_refs(struct THeader *t, const char *source_pos) { struct List *el; struct TRef *ref = NULL; struct THeader *tref; if (t->parent == NULL || hdr2ptr(t->parent) == null_context) { return _talloc_unlink(NULL, hdr2ptr(t->parent), source_pos); } /* check if refs have same parent */ list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent != t->parent) { do_dbg("free_with_refs: parent fail"); return -1; } } /* always same parent, drop one ref */ return _talloc_free(ref, source_pos); } int _talloc_free(const void *ptr, const char *source_pos) { CxMem *cx; struct THeader *t; struct THeader *tparent; size_t orig_size; do_dbg("DBG: talloc_free(%p) (%s)", ptr, talloc_get_name(ptr)); if (!ptr) return -1; t = ptr2hdr(ptr); /* handle multi-parent free */ if (!list_empty(&t->ref_list)) return free_with_refs(t, source_pos); /* set pending flag */ if (has_flags(t, FLAG_PENDING)) return 0; set_flags(t, FLAG_PENDING); /* run destructor */ if (t->destructor && t->destructor((void *)ptr) < 0) { do_dbg("DBG: talloc_free(%s) - destructor failed", talloc_get_name(ptr)); clear_flags(t, FLAG_PENDING); return -1; } list_del(&t->node); free_children(ptr, true, source_pos); tparent = t->parent; orig_size = t->size; cx = t->cx; /* clear & free */ memset(t, 0, THSIZE); t->size = orig_size; t->th_flags = MAGIC_FREE; t->name = source_pos; cx_free(cx, t); apply_memlimit(tparent, -total_size(orig_size), false); return 0; } /* * Refs */ static struct THeader *find_ref_by_parent(struct THeader *t, struct THeader *tparent) { struct List *el; struct THeader *tref; struct TRef *ref; list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent == tparent) return tref; } return NULL; } /* remove TRef from ->ref_list */ static int ref_destructor(void *ptr) { struct TRef *ref = ptr; list_del(&ref->ref_node); if (ref->paired_ref) { ref->paired_ref->paired_ref = NULL; ref->paired_ref = NULL; } return 0; } static struct TRef *new_ref(const void *parent, const char *name) { struct TRef *ref; ref = internal_alloc_prepend(parent, sizeof(struct TRef), name ? name : REF_NAME); if (!ref) return NULL; ref->paired_ref = NULL; list_init(&ref->ref_node); talloc_set_destructor(ref, ref_destructor); return ref; } void *_talloc_reference_named(const void *new_parent, const void *ptr, const char *name) { struct TRef *ref; struct THeader *t; t = ptr2hdr(ptr); if (!t) return NULL; ref = new_ref(new_parent, name); if (!ref) return NULL; list_append(&t->ref_list, &ref->ref_node); return (void *)ptr; } int _talloc_unlink(const void *parent, const void *ptr, const char *source_pos) { struct TRef *ref; struct THeader *tref = NULL; struct THeader *tparent; struct THeader *t; int err; t = ptr2hdr(ptr); if (!t) { do_dbg("_talloc_unlink err: no ptr"); return -1; } tparent = ptr2hdr(parent ? parent : null_context); if (t->parent != tparent) { /* ref is not primary */ tref = find_ref_by_parent(t, tparent); if (tref) { err = _talloc_free(hdr2ptr(tref), source_pos); } else { do_dbg("_talloc_unlink err: find_ref_by_parent failed"); err = -1; } } else if (list_empty(&t->ref_list)) { /* ref is primary and there are no other refs */ err = _talloc_free(ptr, source_pos); } else { /* main parent but refs, move to new parent */ /* use first ref to get new parent */ ref = list_pop_type(&t->ref_list, struct TRef, ref_node); tref = ptr2hdr(ref); list_del(&t->node); /* move */ t->parent = tref->parent; add_child(t->parent, t); /* free ref */ err = _talloc_free(ref, source_pos); } return err; } /* * Parent change */ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr) { struct THeader *tnew; struct THeader *told; struct THeader *t; CxMem *cxnew; t = ptr2hdr(ptr); if (!t) return NULL; tnew = ptr2hdr(new_parent ? new_parent : null_context); told = ptr2hdr(old_parent ? old_parent : null_context); if (tnew == t || tnew == told) return (void *)ptr; cxnew = tnew ? tnew->cx : NULL; /* find ref to change parent of */ if (told != t->parent) { t = find_ref_by_parent(t, told); if (!t) { do_log("talloc_reparent failed: did not find old parent\n"); return NULL; } } /* check cx change */ if (t->cx != cxnew) { return NULL; } /* change parent */ list_del(&t->node); add_child(tnew, t); t->parent = tnew; move_memlimit(t, tnew, told); return (void *)ptr; } void *talloc_steal(const void *new_parent, const void *ptr) { struct THeader *t; t = ptr2hdr(ptr); if (!t) return NULL; /* disallow steal when refs are present */ if (!list_empty(&t->ref_list)) return NULL; return talloc_reparent(hdr2ptr(t->parent), new_parent, ptr); } void *_talloc_move(const void *new_parent, void **ptr_p) { void *ptr; ptr = talloc_steal(new_parent, *ptr_p); if (ptr) *ptr_p = NULL; return ptr; } /* * Realloc */ /* node address has moved */ static void fix_list(struct List *node, struct List *oldnode) { if (node->next == oldnode) { list_init(node); } else { node->next->prev = node; node->prev->next = node; } } void *_talloc_realloc(const void *parent, void *ptr, size_t elem_size, size_t count, const char *name) { struct THeader *t1, *t2; struct List *el; CxMem *this_cx; uint32_t old_flags; ssize_t delta; size_t size; /* calc total size */ if (!safe_mul_size(&size, elem_size, count)) return NULL; if (size > TALLOC_MAXLEN) return NULL; /* posix realloc behaviour */ if (!ptr) { if (size == 0) return NULL; return talloc_named_const(parent, size, name); } else if (size == 0) { if (talloc_unlink(parent, ptr) != 0) if (0) do_log("realloc(size=0): unlink failed\n"); return NULL; } t1 = ptr2hdr(ptr); /* disallow realloc when refs are present */ if (!list_empty(&t1->ref_list)) return NULL; /* size difference */ delta = size - t1->size; if (delta == 0) return ptr; /* check limits */ if (!apply_memlimit(t1->parent, delta, false)) return NULL; /* actual realloc of memory */ this_cx = get_owner_cx(t1); old_flags = t1->th_flags; t1->th_flags = MAGIC_FREE; t2 = cx_realloc(this_cx, t1, total_size(size)); if (!t2) { apply_memlimit(t1->parent, -delta, false); t1->th_flags = old_flags; return NULL; } /* fix header after realloc */ t2->th_flags = old_flags; t2->size = size; t2->name = name; /* was memory moved? */ if (t1 == t2) return ptr; /* fix lists after move */ fix_list(&t2->node, &t1->node); fix_list(&t2->child_list, &t1->child_list); fix_list(&t2->ref_list, &t1->ref_list); list_for_each(el, &t2->child_list) { struct THeader *tchild; tchild = container_of(el, struct THeader, node); tchild->parent = t2; } return hdr2ptr(t2); } void *talloc_realloc_fn(const void *parent, void *ptr, size_t size) { return _talloc_realloc(parent, ptr, 1, size, "talloc_realloc_fn"); } /* * memlimit */ /* apply delta to single context */ static bool apply_memlimit_marked(struct THeader *t, ssize_t delta, bool force) { struct List *el; struct THeader *tlim; struct TLimit *lim = NULL; /* find memlimit struct */ list_for_each(el, &t->child_list) { tlim = container_of(el, struct THeader, node); if (tlim->name == MEMLIMIT_NAME) { lim = hdr2ptr(tlim); goto apply; } } return true; apply: /* check limit */ if (delta > 0 && !force) { if (lim->cur_size + delta > lim->max_size) return false; } /* update parent first */ if (!apply_memlimit(t->parent, delta, force)) return false; /* parent is ok, safe to update current struct */ lim->cur_size += delta; if (lim->cur_size < 0) lim->cur_size = 0; return true; } /* apply delta recursively */ static bool apply_memlimit(struct THeader *parent, ssize_t delta, bool force) { struct THeader *t = parent; while (t && has_flags(t, FLAG_USE_MEMLIMIT)) { if (has_flags(t, FLAG_HAS_MEMLIMIT)) return apply_memlimit_marked(t, delta, force); t = t->parent; } return true; } enum { OP_NONE = 0, OP_SET_MEMLIMIT = 1, OP_CLEAR_MEMLIMIT = 2, }; /* count allocated memory and sync flags */ static size_t memlimit_walk(struct THeader *t, int depth, int op) { struct List *el; struct THeader *tchild; size_t size = 0; if (has_flags(t, FLAG_PENDING)) return 0; /* sync memlimit flags */ if (op == OP_SET_MEMLIMIT) { set_flags(t, FLAG_USE_MEMLIMIT); } else if (op == OP_CLEAR_MEMLIMIT) { if (has_flags(t, FLAG_HAS_MEMLIMIT)) op = OP_NONE; else clear_flags(t, FLAG_USE_MEMLIMIT); } /* avoid too deep recursion */ if (depth > TALLOC_MAX_DEPTH) return t->size; /* recurse info child_list */ set_flags(t, FLAG_PENDING); list_for_each(el, &t->child_list) { tchild = container_of(el, struct THeader, node); size += memlimit_walk(tchild, depth + 1, op); } clear_flags(t, FLAG_PENDING); return size + t->size; } static void move_memlimit(struct THeader *t, struct THeader *new_parent, struct THeader *old_parent) { bool oldlim, newlim; ssize_t delta; int op = OP_NONE; /* is memlimit in use? */ newlim = new_parent && has_flags(new_parent, FLAG_USE_MEMLIMIT); oldlim = old_parent && has_flags(old_parent, FLAG_USE_MEMLIMIT); if (!oldlim && !newlim) return; if (oldlim && !newlim) op = OP_CLEAR_MEMLIMIT; else if (newlim && !oldlim) op = OP_SET_MEMLIMIT; /* yes, calc memory size */ delta = memlimit_walk(t, 0, op); /* subtract from old parent */ if (oldlim) apply_memlimit(old_parent, -delta, true); if (newlim) { /* add to new parent */ apply_memlimit(new_parent, delta, true); set_flags(t, FLAG_USE_MEMLIMIT); } else if (!has_flags(t, FLAG_HAS_MEMLIMIT)) { /* drop flag to avoid unnecessary walks */ clear_flags(t, FLAG_USE_MEMLIMIT); } } /* configure memory limits */ int talloc_set_memlimit(const void *ptr, size_t max_size) { struct TLimit *lim = NULL; struct THeader *t, *tmp; struct List *el; if (!ptr) return -1; t = ptr2hdr(ptr); /* find TLimit struct */ if (has_flags(t, FLAG_HAS_MEMLIMIT)) { list_for_each(el, &t->child_list) { tmp = container_of(el, struct THeader, node); if (tmp->name == MEMLIMIT_NAME) { lim = hdr2ptr(tmp); break; } } } /* disable memlimit */ if (max_size == 0) { clear_flags(t, FLAG_HAS_MEMLIMIT); if (lim) talloc_free(lim); return 0; } /* allocate new object */ if (!lim) { lim = internal_alloc_prepend(ptr, sizeof(struct TLimit), MEMLIMIT_NAME); if (!lim) return -1; } /* configure */ lim->max_size = max_size; lim->cur_size = 0; set_flags(t, FLAG_USE_MEMLIMIT | FLAG_HAS_MEMLIMIT); return 0; } /* * Name handling */ const char *talloc_get_name(const void *ptr) { struct THeader *t = ptr2hdr(ptr); return (t && t->name) ? t->name : UNNAMED_NAME; } const char *talloc_set_name(const void *ptr, const char *fmt, ...) { va_list ap; struct THeader *t; t = ptr2hdr(ptr); if (t) { va_start(ap, fmt); t->name = talloc_vasprintf(ptr, fmt, ap); va_end(ap); return t->name; } return NULL; } void talloc_set_name_const(const void *ptr, const char *name) { struct THeader *t; t = ptr2hdr(ptr); if (t) t->name = name; } void *talloc_check_name(const void *ptr, const char *name) { const char *curname; curname = talloc_get_name(ptr); if (curname && name && strcmp(name, curname) == 0) return (void *)ptr; return NULL; } void *_talloc_get_type_abort(const void *ptr, const char *name) { void *res; res = talloc_check_name(ptr, name); if (res) return (void *)ptr; do_abort("wrong type"); } /* * Info */ size_t talloc_get_size(const void *ptr) { struct THeader *t; if (!ptr) return 0; t = ptr2hdr(ptr); return t->size; } bool talloc_is_parent(const void *parent, const void *ptr) { struct THeader *tc; struct THeader *tp; int count = 0; if (!ptr || !parent) return false; tp = ptr2hdr(parent); tc = ptr2hdr(ptr); while (tc) { if (tc->parent == tp) return true; tc = tc->parent; /* dont bother too much */ if (++count >= TALLOC_MAX_DEPTH) break; } return false; } size_t talloc_reference_count(const void *ptr) { struct List *el; size_t cnt = 0; struct THeader *t; t = ptr2hdr(ptr); if (t) { list_for_each(el, &t->ref_list) cnt++; } return cnt; } struct BytesAndCount { size_t bytes; size_t count; }; static void calc_bytes_and_count(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg) { struct BytesAndCount *state = cb_arg; state->count++; if (!is_ref) { state->bytes += talloc_get_size(ptr); } } size_t talloc_total_size(const void *ptr) { struct BytesAndCount state; memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); return state.bytes; } size_t talloc_total_blocks(const void *ptr) { struct BytesAndCount state; memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); return state.count; } void *talloc_parent(const void *ptr) { struct THeader *t; t = ptr2hdr(ptr); if (t && t->parent) return hdr2ptr(t->parent); return NULL; } const char *talloc_parent_name(const void *ptr) { return talloc_get_name(talloc_parent(ptr)); } void *talloc_find_parent_byname(const void *ptr, const char *name) { struct List *el; struct THeader *tref; struct TRef *ref; struct THeader *t; t = ptr2hdr(ptr); if (!t || !name) return NULL; if (t->parent && !strcmp(name, t->parent->name)) return hdr2ptr(t->parent); list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent && !strcmp(name, tref->parent->name)) return hdr2ptr(tref->parent); } return NULL; } /* * String copy */ void *talloc_memdup(const void *parent, const void *src, size_t len) { void *res = NULL; if (src) { res = talloc_named_const(parent, len, "talloc_memdup"); if (res) memcpy(res, src, len); } return res; } char *talloc_strdup(const void *parent, const char *s) { return talloc_strndup(parent, s, TALLOC_MAXLEN); } char *talloc_strndup(const void *parent, const char *s, size_t maxlen) { size_t len; char *res; if (!s) return NULL; len = strnlen(s, maxlen); res = talloc_named_const(parent, len + 1, NULL); if (!res) return NULL; memcpy(res, s, len); res[len] = 0; talloc_set_name_const(res, res); return res; } /* * string append */ static size_t buffer_strlen(const void *ptr) { size_t len = talloc_get_size(ptr); return len ? len - 1 : 0; } static char *_concat(char *ptr, bool isbuf, const char *s, size_t maxlen) { size_t plen; size_t slen; /* simple cases */ if (!ptr) return talloc_strndup(ptr, s, maxlen); if (!s) return ptr; /* get lengths */ if (isbuf) { plen = buffer_strlen(ptr); } else { plen = strnlen(ptr, talloc_get_size(ptr)); } slen = strnlen(s, maxlen); /* resize and copy */ ptr = talloc_realloc_fn(ptr, ptr, plen + slen + 1); if (!ptr) return NULL; memcpy(ptr + plen, s, slen + 1); talloc_set_name_const(ptr, ptr); return ptr; } char *talloc_strdup_append(char *ptr, const char *s) { return _concat(ptr, false, s, TALLOC_MAXLEN); } char *talloc_strdup_append_buffer(char *ptr, const char *s) { return _concat(ptr, true, s, TALLOC_MAXLEN); } char *talloc_strndup_append(char *ptr, const char *s, size_t maxlen) { return _concat(ptr, false, s, maxlen); } char *talloc_strndup_append_buffer(char *ptr, const char *s, size_t maxlen) { return _concat(ptr, true, s, maxlen); } /* * printfs */ _PRINTF(4,0) static char *_tprintf(const void *parent, char *ptr, size_t plen, const char *fmt, va_list ap) { char buf[128]; ssize_t len; va_list ap2; char *res; /* print into temp buffer */ va_copy(ap2, ap); len = vsnprintf(buf, sizeof(buf), fmt, ap2); va_end(ap2); if (len < 0) return NULL; /* reserve room */ res = talloc_realloc_fn(parent, ptr, plen + len + 1); if (!res) return NULL; /* fill with string */ if (len < (int)sizeof(buf)) { memcpy(res + plen, buf, len + 1); } else { va_copy(ap2, ap); vsnprintf(res + plen, len + 1, fmt, ap2); va_end(ap2); } talloc_set_name_const(res, res); return res; } char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap) { return _tprintf(parent, NULL, 0, fmt, ap); } char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap) { size_t plen = strnlen(ptr, talloc_get_size(ptr)); return _tprintf(NULL, ptr, plen, fmt, ap); } char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap) { size_t plen = buffer_strlen(ptr); return _tprintf(NULL, ptr, plen, fmt, ap); } char *talloc_asprintf(const void *parent, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf(parent, fmt, ap); va_end(ap); return res; } char *talloc_asprintf_append(char *ptr, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf_append(ptr, fmt, ap); va_end(ap); return res; } char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf_append_buffer(ptr, fmt, ap); va_end(ap); return res; } /* * Autofree */ /* run on program exit */ static void autofree_handler(void) { TALLOC_FREE(autofree_ctx); } static int autofree_destructor(void *ptr) { autofree_ctx = NULL; return 0; } /* create context, register handler with atexit */ void *talloc_autofree_context(void) { static int atexit_ok; /* register atexit handler */ if (!atexit_ok) { if (atexit(autofree_handler) != 0) return NULL; atexit_ok = 1; } /* initialize autofree top-level context */ if (!autofree_ctx) { autofree_ctx = talloc_named_const(NULL, 0, AUTOFREE_NAME); if (!autofree_ctx) return NULL; talloc_set_destructor(autofree_ctx, autofree_destructor); } return autofree_ctx; } /* * Logging */ static void log_to_stderr(const char *message) { fprintf(stderr, "%s\n", message); } _PRINTF(1, 2) static void do_log(const char *fmt, ...) { char buf[128]; va_list ap; if (!_log_cb) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); _log_cb(buf); } _PRINTF(1, 2) static void do_dbg(const char *fmt, ...) { char buf[128]; va_list ap; if (!_log_cb || debug_level == 0) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); _log_cb(buf); } _PRINTF(1, 0) static void do_abort(const char *fmt, ...) { char buf[128]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (_abort_cb) _abort_cb(buf); else if (_log_cb) _log_cb(buf); abort(); } void talloc_set_log_fn(void (*log_fn)(const char *message)) { _log_cb = log_fn; } void talloc_set_log_stderr(void) { _log_cb = log_to_stderr; } void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) { _abort_cb = abort_fn; } /* * Tracking. */ static void atexit_leak_report(void) { if (leak_report == 2) talloc_report_full(NULL, stderr); else talloc_report(NULL, stderr); } /* activate null context as child of autofree context */ void talloc_enable_null_tracking(void) { void *ctx; if (!null_context) { ctx = talloc_named_const(NULL, 0, NULL_NAME); if (ctx && autofree_ctx) talloc_reparent(NULL, ctx, autofree_ctx); null_context = ctx; } } /* activate null context */ void talloc_enable_null_tracking_no_autofree(void) { if (!null_context) null_context = talloc_named_const(NULL, 0, NULL_NAME); } /* move childs away from null context */ void talloc_disable_null_tracking(void) { struct THeader *t, *tchild; struct List *el, *tmp; if (!null_context) return; t = ptr2hdr(null_context); list_for_each_safe(el, &t->child_list, tmp) { tchild = container_of(el, struct THeader, node); list_del(&tchild->node); tchild->parent = NULL; } TALLOC_FREE(null_context); } void talloc_enable_leak_report(void) { if (!leak_report) atexit(atexit_leak_report); leak_report = 1; } void talloc_enable_leak_report_full(void) { if (!leak_report) atexit(atexit_leak_report); leak_report = 2; } /* * Reporting */ static void *find_ptr_from_ref(const struct TRef *ref) { struct List *el; struct TRef *ref2; struct THeader *tref2; struct THeader *t; list_for_each(el, &ref->ref_node) { /* * Actual struct is not known here - THeader * must have both ->destructor & ->ref_list * accessible locations. */ ref2 = container_of(el, struct TRef, ref_node); /* this check must work it out */ tref2 = ((struct THeader *)ref2) - 1; if (hdr_is_ref(tref2)) continue; /* it is not TRef, so it must be parent's THeader */ t = container_of(el, struct THeader, ref_list); return hdr2ptr(t); } return NULL; } void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*cb_func)(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg), void *cb_arg) { struct List *el; struct THeader *tchild; struct THeader *t; t = ptr2hdr(ptr); if (!t) t = ptr2hdr(null_context); if (!t) return; if (has_flags(t, FLAG_PENDING)) return; /* run callback */ if (hdr_is_ref(t)) { void *ptr2 = find_ptr_from_ref(ptr); if (ptr2) cb_func(ptr2, depth, max_depth, true, cb_arg); return; } cb_func(ptr, depth, max_depth, false, cb_arg); /* check depth */ depth++; if (depth > max_depth) return; /* loop over childs */ set_flags(t, FLAG_PENDING); list_for_each(el, &t->child_list) { tchild = container_of(el, struct THeader, node); talloc_report_depth_cb(hdr2ptr(tchild), depth, max_depth, cb_func, cb_arg); } clear_flags(t, FLAG_PENDING); } static void report_cb(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg) { FILE *f = cb_arg; struct BytesAndCount state; const char *name; int indent; char limitbuf[128]; struct THeader *t; indent = depth * 2; t = ptr2hdr(ptr); name = talloc_get_name(ptr); limitbuf[0] = 0; if (name == MEMLIMIT_NAME) { struct TLimit *lim = hdr2ptr(t); snprintf(limitbuf, sizeof(limitbuf), "%s [cur=%" PRIuZ " max=%" PRIuZ "]", name, lim->cur_size, lim->max_size); name = limitbuf; } memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); if (depth == 0) { fprintf(f, "talloc report on '%s' (total %" PRIuZ " bytes in %" PRIuZ " blocks)%s\n", name, state.bytes, state.count, limitbuf); return; } if (is_ref) { fprintf(f, "%*sreference to %s\n", indent, " ", name); return; } fprintf(f, "%*s%-*s contains %6" PRIuZ " bytes in %6" PRIuZ " blocks%s\n", indent, " ", indent < 40 ? 40 - indent : 0, name, state.bytes, state.count, limitbuf); } void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) { talloc_report_depth_cb(ptr, depth, max_depth, report_cb, f); } void talloc_report(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, 1, f); } void talloc_report_full(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, TALLOC_MAX_DEPTH, f); } void talloc_show_parents(const void *ptr, FILE *file) { struct THeader *t, *tref; struct TRef *ref; struct List *el; if (!ptr) { fprintf(file, "No parents for NULL\n"); return; } fprintf(file, "Parents for '%s'\n", talloc_get_name(ptr)); t = ptr2hdr(ptr); if (t->parent) { fprintf(file, "\t%s\n", talloc_get_name(hdr2ptr(t->parent))); } else { fprintf(file, "\tNULL context\n"); } list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); fprintf(file, "\t%s\n", talloc_get_name(hdr2ptr(tref->parent))); } } /* * Talloc-backed CxMem. * * Makes CxMem modules work with talloc. */ static void *cxt_alloc(void *ctx, size_t size) { return talloc_size(ctx, size); } static void cxt_free(void *ctx, const void *ptr) { if (talloc_unlink(ctx, ptr) != 0) do_log("cxt_free: talloc_unlink failed\n"); } static void *cxt_realloc(void *ctx, void *ptr, size_t len) { return talloc_realloc_size(ctx, ptr, len); } static void cxt_destroy(void *ctx) { if (talloc_free(ctx) != 0) do_log("cxt_destroy: talloc_free failed\n"); } static const struct CxOps cxt_ops = { cxt_alloc, cxt_realloc, cxt_free, cxt_destroy, }; CxMem *talloc_as_cx(const void *parent, const char *name) { struct CxMem *cx; if (!name) name = ".cxmem"; cx = talloc_named_const(parent, sizeof(struct CxMem), name); if (!cx) return NULL; cx->ops = &cxt_ops; cx->ctx = cx; return cx; } pgqd/lib/usual/safeio.h0000664000401600040160000000324113175113172013372 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * EINTR-safe I/O functions. */ #ifndef _USUAL_SAFEIO_H_ #define _USUAL_SAFEIO_H_ #include /** read */ int safe_read(int fd, void *buf, int len) _MUSTCHECK; /** write */ int safe_write(int fd, const void *buf, int len) _MUSTCHECK; /** recv */ int safe_recv(int fd, void *buf, int len, int flags) _MUSTCHECK; /** send */ int safe_send(int fd, const void *buf, int len, int flags) _MUSTCHECK; /** close */ int safe_close(int fd); /** recvmsg */ int safe_recvmsg(int fd, struct msghdr *msg, int flags) _MUSTCHECK; /** sendmsg */ int safe_sendmsg(int fd, const struct msghdr *msg, int flags) _MUSTCHECK; /** connect */ int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len) _MUSTCHECK; /** accept */ int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len) _MUSTCHECK; #endif pgqd/lib/usual/pthread.h0000664000401600040160000000401213175113172013550 0ustar cbecbe/* * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Pthreads compat for win32. */ #ifndef _USUAL_PTHREAD_H_ #define _USUAL_PTHREAD_H_ #include #ifdef HAVE_PTHREAD_H #include #else #ifdef WIN32 #define pthread_create(a,b,c,d) compat_pthread_create(a,b,c,d) #define pthread_mutex_init(a,b) compat_pthread_mutex_init(a,b) #define pthread_mutex_destroy(a) compat_pthread_mutex_destroy(a) #define pthread_mutex_lock(a) compat_pthread_mutex_lock(a) #define pthread_mutex_unlock(a) compat_pthread_mutex_unlock(a) #define pthread_join(a,b) compat_pthread_join(a,b) #define pthread_once(a,b) compat_pthread_once(a,b) typedef HANDLE pthread_t; typedef HANDLE pthread_mutex_t; typedef int pthread_attr_t; int pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*fn)(void *), void *arg); int pthread_mutex_init(pthread_mutex_t *lock, void *unused); int pthread_mutex_destroy(pthread_mutex_t *lock); int pthread_mutex_lock(pthread_mutex_t *lock); int pthread_mutex_unlock(pthread_mutex_t *lock); int pthread_join(pthread_t *t, void **ret); #ifdef INIT_ONCE_STATIC_INIT #define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT typedef INIT_ONCE pthread_once_t; int pthread_once(pthread_once_t *once, void (*once_func)(void)); #endif #endif /* WIN32 */ #endif /* HAVE_PTHREAD_H */ #endif pgqd/lib/usual/misc.h0000664000401600040160000000307713175113172013066 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Random stuff that does not fit elsewhere. */ #ifndef _USUAL_MISC_H_ #define _USUAL_MISC_H_ #include #ifdef WORDS_BIGENDIAN #define FOURCC(a,b,c,d) \ ( ((unsigned int)(unsigned char)(a) << 24) \ | ((unsigned int)(unsigned char)(b) << 16) \ | ((unsigned int)(unsigned char)(c) << 8) \ | ((unsigned int)(unsigned char)(d))) #else /** Four-byte identifier as integer */ #define FOURCC(a,b,c,d) \ ( ((unsigned int)(unsigned char)(a)) \ | ((unsigned int)(unsigned char)(b) << 8) \ | ((unsigned int)(unsigned char)(c) << 16) \ | ((unsigned int)(unsigned char)(d) << 24)) #endif #if defined(__i386__) || defined(__x86_64__) #define mb() asm volatile("mfence":::"memory") #define rmb() asm volatile("lfence":::"memory") #define wmb() asm volatile("sfence":::"memory") #endif #endif pgqd/lib/usual/mbuf.h0000664000401600040160000001726513175113172013070 0ustar cbecbe /** \file * Safe and easy access to memory buffer. */ #ifndef _USUAL_MBUF_H_ #define _USUAL_MBUF_H_ #include #include /** MBuf structure. Allocated by user, can be in stack. */ struct MBuf { uint8_t *data; unsigned read_pos; unsigned write_pos; unsigned alloc_len; bool reader; bool fixed; }; /** Format fragment for *printf() */ #define MBUF_FMT ".*s" /** Argument layout for *printf() */ #define MBUF_ARG(m) ((m) ? mbuf_written(m) : 6), ((m) ? (const char *)mbuf_data(m) : "(null)") /* * Init functions */ /** Initialize R/O buffer to fixed memory area. */ static inline void mbuf_init_fixed_reader(struct MBuf *buf, const void *ptr, unsigned len) { buf->data = (uint8_t *)ptr; buf->read_pos = 0; buf->write_pos = len; buf->alloc_len = len; buf->reader = true; buf->fixed = true; } /** Initialize R/W buffer to fixed memory area. */ static inline void mbuf_init_fixed_writer(struct MBuf *buf, void *ptr, unsigned len) { buf->data = (uint8_t *)ptr; buf->read_pos = 0; buf->write_pos = 0; buf->alloc_len = len; buf->reader = false; buf->fixed = true; } /** Initialize R/W buffer to dynamically allocated memory area. */ static inline void mbuf_init_dynamic(struct MBuf *buf) { buf->data = NULL; buf->read_pos = 0; buf->write_pos = 0; buf->alloc_len = 0; buf->reader = false; buf->fixed = false; } /** Free dynamically allocated area, if exists. */ static inline void mbuf_free(struct MBuf *buf) { if (buf->data) { if (!buf->fixed) free(buf->data); memset(buf, 0, sizeof(*buf)); } } /* * Reset functions. */ /** Move read cursor to start of buffer. */ static inline void mbuf_rewind_reader(struct MBuf *buf) { buf->read_pos = 0; } /** Move both read and write cursor to start of buffer. */ static inline void mbuf_rewind_writer(struct MBuf *buf) { if (!buf->reader) { buf->read_pos = 0; buf->write_pos = 0; } } /* * Info functions. */ /** How many bytes can be read with read cursor. */ static inline unsigned mbuf_avail_for_read(const struct MBuf *buf) { return buf->write_pos - buf->read_pos; } /** How many bytes can be written with write cursor, without realloc. */ static inline unsigned mbuf_avail_for_write(const struct MBuf *buf) { if (!buf->reader && buf->alloc_len > buf->write_pos) return buf->alloc_len - buf->write_pos; return 0; } /** How many data bytes are in buffer. */ static inline unsigned mbuf_written(const struct MBuf *buf) { return buf->write_pos; } /** How many bytes have been read from buffer */ static inline unsigned mbuf_consumed(const struct MBuf *buf) { return buf->read_pos; } /** Return pointer to data area. */ static inline const void *mbuf_data(const struct MBuf *buf) { return buf->data; } /** Do the mbufs contain same data. */ static inline bool mbuf_eq(const struct MBuf *buf1, const struct MBuf *buf2) { if (buf1 == buf2) return true; if (!buf1 || !buf2 || (mbuf_written(buf1) != mbuf_written(buf2))) return false; return memcmp(mbuf_data(buf1), mbuf_data(buf2), mbuf_written(buf1)) == 0; } /** Complare mbuf to asciiz string */ static inline bool mbuf_eq_str(const struct MBuf *buf1, const char *s) { struct MBuf tmp; mbuf_init_fixed_reader(&tmp, s, strlen(s)); return mbuf_eq(buf1, &tmp); } /* * Read functions. */ /** Read a byte from read cursor. */ _MUSTCHECK static inline bool mbuf_get_byte(struct MBuf *buf, uint8_t *dst_p) { if (buf->read_pos + 1 > buf->write_pos) return false; *dst_p = buf->data[buf->read_pos++]; return true; } /** Read big-endian uint16 from read cursor. */ _MUSTCHECK static inline bool mbuf_get_char(struct MBuf *buf, char *dst_p) { if (buf->read_pos + 1 > buf->write_pos) return false; *dst_p = buf->data[buf->read_pos++]; return true; } _MUSTCHECK static inline bool mbuf_get_uint16be(struct MBuf *buf, uint16_t *dst_p) { unsigned a, b; if (buf->read_pos + 2 > buf->write_pos) return false; a = buf->data[buf->read_pos++]; b = buf->data[buf->read_pos++]; *dst_p = (a << 8) | b; return true; } /** Read big-endian uint32 from read cursor. */ _MUSTCHECK static inline bool mbuf_get_uint32be(struct MBuf *buf, uint32_t *dst_p) { unsigned a, b, c, d; if (buf->read_pos + 4 > buf->write_pos) return false; a = buf->data[buf->read_pos++]; b = buf->data[buf->read_pos++]; c = buf->data[buf->read_pos++]; d = buf->data[buf->read_pos++]; *dst_p = (a << 24) | (b << 16) | (c << 8) | d; return true; } /** Get reference to len bytes from read cursor. */ _MUSTCHECK static inline bool mbuf_get_uint64be(struct MBuf *buf, uint64_t *dst_p) { uint32_t a, b; if (!mbuf_get_uint32be(buf, &a) || !mbuf_get_uint32be(buf, &b)) return false; *dst_p = ((uint64_t)a << 32) | b; return true; } _MUSTCHECK static inline bool mbuf_get_bytes(struct MBuf *buf, unsigned len, const uint8_t **dst_p) { if (buf->read_pos + len > buf->write_pos) return false; *dst_p = buf->data + buf->read_pos; buf->read_pos += len; return true; } /** Get reference to asciiz string from read cursor. */ _MUSTCHECK static inline bool mbuf_get_chars(struct MBuf *buf, unsigned len, const char **dst_p) { if (buf->read_pos + len > buf->write_pos) return false; *dst_p = (char *)buf->data + buf->read_pos; buf->read_pos += len; return true; } _MUSTCHECK static inline bool mbuf_get_string(struct MBuf *buf, const char **dst_p) { const char *res = (char *)buf->data + buf->read_pos; const uint8_t *nul = memchr(res, 0, mbuf_avail_for_read(buf)); if (!nul) return false; *dst_p = res; buf->read_pos = nul + 1 - buf->data; return true; } /* * Write functions. */ /** Allocate more room if needed and the mbuf allows. */ _MUSTCHECK bool mbuf_make_room(struct MBuf *buf, unsigned len); /** Write a byte to write cursor. */ _MUSTCHECK static inline bool mbuf_write_byte(struct MBuf *buf, uint8_t val) { if (buf->write_pos + 1 > buf->alloc_len && !mbuf_make_room(buf, 1)) return false; buf->data[buf->write_pos++] = val; return true; } /** Write len bytes to write cursor. */ _MUSTCHECK static inline bool mbuf_write(struct MBuf *buf, const void *ptr, unsigned len) { if (buf->write_pos + len > buf->alloc_len && !mbuf_make_room(buf, len)) return false; memcpy(buf->data + buf->write_pos, ptr, len); buf->write_pos += len; return true; } /** writes full contents of another mbuf, without touching it */ _MUSTCHECK static inline bool mbuf_write_raw_mbuf(struct MBuf *dst, struct MBuf *src) { return mbuf_write(dst, src->data, src->write_pos); } /** writes partial contents of another mbuf, with touching it */ _MUSTCHECK static inline bool mbuf_write_mbuf(struct MBuf *dst, struct MBuf *src, unsigned len) { const uint8_t *data; if (!mbuf_get_bytes(src, len, &data)) return false; if (!mbuf_write(dst, data, len)) { src->read_pos -= len; return false; } return true; } /** Fiil mbuf with byte value */ _MUSTCHECK static inline bool mbuf_fill(struct MBuf *buf, uint8_t byte, unsigned len) { if (buf->write_pos + len > buf->alloc_len && !mbuf_make_room(buf, len)) return false; memset(buf->data + buf->write_pos, byte, len); buf->write_pos += len; return true; } /** remove some data from mbuf */ _MUSTCHECK static inline bool mbuf_cut(struct MBuf *buf, unsigned ofs, unsigned len) { if (buf->reader) return false; if (ofs + len < buf->write_pos) { unsigned endofs = ofs + len; memmove(buf->data + ofs, buf->data + endofs, buf->write_pos - endofs); buf->write_pos -= len; } else if (ofs < buf->write_pos) { buf->write_pos = ofs; } return true; } static inline void mbuf_copy(const struct MBuf *src, struct MBuf *dst) { *dst = *src; } _MUSTCHECK static inline bool mbuf_slice(struct MBuf *src, unsigned len, struct MBuf *dst) { if (len > mbuf_avail_for_read(src)) return false; mbuf_init_fixed_reader(dst, src->data + src->read_pos, len); src->read_pos += len; return true; } #endif pgqd/lib/usual/hashtab-impl.h0000664000401600040160000001352213175113172014500 0ustar cbecbe/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Simple customizable hashtable implementation. * * - Fixed-size hash table, open-addressed * - Extended by linking several together * - Resizable by copying. * - Can be lockless in multi-reader, one-writer situation if * mempory barrier macros are defined. This also requires that * HashItem must not be split across cachelines. */ #include #include #ifndef HTAB_KEY_T /** Overridable type for key */ #define HTAB_KEY_T unsigned long #endif #ifndef HTAB_VAL_T /** Overridable type for value */ #define HTAB_VAL_T void * #endif #ifndef HTAB_RMB #define HTAB_RMB #endif #ifndef HTAB_WMB #define HTAB_WMB #endif /** Typedef for key */ typedef HTAB_KEY_T htab_key_t; /** Typedef for value */ typedef HTAB_VAL_T htab_val_t; #ifndef HTAB_ITEM #define HTAB_ITEM /** HashTab slot */ struct HashItem { htab_key_t key; htab_val_t value; }; #endif /** Signature for comparision function */ typedef bool (*hash_cmp_fn)(const htab_val_t curval, const void *arg); #ifndef HTAB_MAX_FILL /** Max fill percentage */ #define HTAB_MAX_FILL 75 #endif #define MASK(h) ((h)->size - 1) #define CALC_POS(h, key) ((key) & MASK(h)) #define NEXT_POS(h, pos) (((pos) * 5 + 1) & MASK(h)) #define MAX_USED(h) ((h)->size * HTAB_MAX_FILL / 100) /** Single HashTab segment */ struct HashTab { struct HashTab *next; hash_cmp_fn cmp_fn; CxMem *ca; unsigned size; unsigned used; struct HashItem tab[FLEX_ARRAY]; }; /** Initialize HashTab */ static struct HashTab *hashtab_create(unsigned size, hash_cmp_fn cmp_fn, CxMem *ca) { struct HashTab *h; unsigned len = size * sizeof(struct HashItem) + offsetof(struct HashTab, tab); h = cx_alloc0(ca, len); if (h) { h->size = size; h->cmp_fn = cmp_fn; h->ca = ca; } return h; } /** Free HashTab */ static void hashtab_destroy(struct HashTab *h) { struct HashTab *tmp; while (h) { tmp = h->next; cx_free(h->ca, h); h = tmp; } } /** Element lookup, optionally inserting new slot */ static htab_val_t *hashtab_lookup(struct HashTab *h, htab_key_t key, bool do_insert, const void *arg) { unsigned pos; struct HashItem *i; loop: /* find key, starting from pos */ pos = CALC_POS(h, key); while (h->tab[pos].value) { i = &h->tab[pos]; HTAB_RMB; if (i->key == key) { if (arg && h->cmp_fn(i->value, arg)) return &i->value; } pos = NEXT_POS(h, pos); } /* not found in this one, check chained tables */ if (h->next) { h = h->next; goto loop; } /* just lookup? */ if (!do_insert) return NULL; /* insert */ if (h->used >= MAX_USED(h)) { struct HashTab *tmp; tmp = hashtab_create(h->size, h->cmp_fn, h->ca); if (!tmp) return NULL; h->next = tmp; h = tmp; pos = CALC_POS(h, key); } h->used++; h->tab[pos].key = key; HTAB_WMB; return &h->tab[pos].value; } /* if proper pos is between src and dst, cannot move */ static bool _hashtab_slot_can_move(struct HashTab *h, unsigned dstpos, unsigned srcpos) { htab_key_t key = h->tab[srcpos].key; unsigned pos, kpos = CALC_POS(h, key); if (kpos == srcpos) return false; if (kpos == dstpos) return true; for (pos = NEXT_POS(h, dstpos); pos != srcpos; pos = NEXT_POS(h, pos)) { if (pos == kpos) return false; } return true; } /** Delete an element */ static void hashtab_delete(struct HashTab *h, htab_key_t key, void *arg) { htab_val_t *vptr; struct HashItem *hd; unsigned pos, dstpos; /* find it */ vptr = hashtab_lookup(h, key, false, arg); if (!vptr) return; /* find right tab */ hd = container_of(vptr, struct HashItem, value); while (h && ((hd < h->tab) || (hd >= h->tab + h->size))) h = h->next; /* calculate index */ dstpos = hd - h->tab; loop: /* move slot */ for (pos = NEXT_POS(h, dstpos); h->tab[pos].value; pos = NEXT_POS(h, pos)) { if (_hashtab_slot_can_move(h, dstpos, pos)) { h->tab[dstpos].key = h->tab[pos].key; h->tab[dstpos].value = h->tab[pos].value; dstpos = pos; goto loop; } } h->tab[dstpos].value = 0; HTAB_WMB; h->tab[dstpos].key = 0; h->used--; } /** Count elements and fragments */ static void hashtab_stats(struct HashTab *h, unsigned *nitem_p, unsigned *ntab_p) { unsigned n = 0, l = 0; while (h) { l++; n += h->used; h = h->next; } *nitem_p = n; *ntab_p = l; } /** Copy elements to new hashtab, perhaps with different size */ static struct HashTab *hashtab_copy(struct HashTab *h_old, unsigned newsize) { struct HashTab *h_new; unsigned i; h_new = hashtab_create(newsize, h_old->cmp_fn, h_old->ca); for (; h_old; h_old = h_old->next) { for (i = 0; i < h_old->size; i++) { struct HashItem *s = &h_old->tab[i]; htab_val_t *new_pos; if (s->value) { new_pos = hashtab_lookup(h_new, s->key, true, NULL); if (!new_pos) goto err; *new_pos = s->value; } } } return h_new; err: hashtab_destroy(h_new); return NULL; } /* example, and avoid "unused" warnings */ static inline void _hashtab_example(void) { unsigned nitem, nlink; struct HashTab *h, *h2; h = hashtab_create(1024, NULL, NULL); hashtab_lookup(h, 123, true, NULL); hashtab_stats(h, &nitem, &nlink); h2 = hashtab_copy(h, 2048); hashtab_delete(h, 123, NULL); hashtab_destroy(h); hashtab_destroy(h2); } pgqd/lib/build_msvc.mk0000664000401600040160000000043513175113172013304 0ustar cbecbe AM_FEATURES = msvc # make it work also when included from test/Makefile top_srcdir = $(dir $(filter %build.mk, $(MAKEFILE_LIST))) top_builddir = $(top_srcdir) abs_top_srcdir := $(abspath $(top_srcdir)) abs_top_builddir := $(abs_top_srcdir) include $(abs_top_srcdir)/mk/antimake.mk pgqd/Makefile0000664000401600040160000000230613175113043011512 0ustar cbecbe -include config.mak PG_CONFIG ?= pg_config PG_INCDIR = $(shell $(PG_CONFIG) --includedir) PG_LIBDIR = $(shell $(PG_CONFIG) --libdir) bin_PROGRAMS = pgqd pgqd_SOURCES = src/pgqd.c src/maint.c src/ticker.c src/retry.c src/pgqd.h nodist_pgqd_SOURCES = pgqd.ini.h pgqd_CPPFLAGS = -I$(PG_INCDIR) -Isrc -I. pgqd_LDFLAGS = -L$(PG_LIBDIR) pgqd_LIBS = -lpq -lm pgqd_EMBED_LIBUSUAL = 1 USUAL_DIR = lib AM_FEATURES = libusual EXTRA_DIST = pgqd.ini CLEANFILES = pgqd.ini.h CONFIG_H = $(USUAL_DIR)/lib/usual/config.h include $(USUAL_DIR)/mk/antimake.mk pgqd.ini.h: pgqd.ini sed -e 's/.*/"&\\n"/' $< > $@ install: install-conf install-conf: mkdir -p '$(DESTDIR)$(docdir)/conf' $(INSTALL) -m 644 pgqd.ini '$(DESTDIR)$(docdir)/conf/pgqd.ini.templ' tags: ctags src/*.[ch] lib/usual/*.[ch] configure: ./autogen.sh #config.mak: configure # ./configure deb: configure debuild -us -uc -b *.o: $(CONFIG_H) $(CONFIG_H): $(error Please run ./configure first) xclean: clean rm -f config.mak config.guess config.sub config.log config.sub config.status rm -f configure install-sh lib/usual/config.h rm -rf debian/.debhelper debian/pgqd rm -f debian/files debian/*-stamp debian/*.debhelper debian/*.log debian/*.substvars pgqd/pgqd.ini0000664000401600040160000000110313175113043011500 0ustar cbecbe[pgqd] # where to log logfile = ~/log/pgqd.log # pidfile pidfile = ~/pid/pgqd.pid ## optional parameters ## # libpq connect string without dbname= #base_connstr = # startup db to query other databases #initial_database = template1 # limit ticker to specific databases #database_list = # log into syslog #syslog = 1 #syslog_ident = pgqd ## optional timeouts ## # how often to check for new databases #check_period = 60 # how often to flush retry queue #retry_period = 30 # how often to do maintentance #maint_period = 120 # how often to run ticker #ticker_period = 1 pgqd/src/0000775000401600040160000000000013175113043010640 5ustar cbecbepgqd/src/maint.c0000664000401600040160000001734413175113043012125 0ustar cbecbe#include "pgqd.h" #include #include #include struct MaintOp { struct List head; const char *func_name; const char *func_arg; }; static struct MaintOp *next_op(struct PgDatabase *db) { struct List *el = statlist_pop(&db->maint_op_list); if (!el) return NULL; return container_of(el, struct MaintOp, head); } static void free_op(struct MaintOp *op) { if (op) { free(op->func_name); free(op->func_arg); free(op); } } void free_maint(struct PgDatabase *db) { struct MaintOp *op; strlist_free(db->maint_item_list); db->maint_item_list = NULL; while ((op = next_op(db)) != NULL) { free_op(op); } free_op(db->cur_maint); db->cur_maint = NULL; } static void close_maint(struct PgDatabase *db, double sleep_time) { log_debug("%s: close_maint, %f", db->name, sleep_time); db->maint_state = DB_CLOSED; pgs_reconnect(db->c_maint, sleep_time); } static void run_test_version(struct PgDatabase *db) { const char *q = "select 1 from pg_proc p, pg_namespace n" " where p.pronamespace = n.oid" " and p.proname = 'maint_operations'" " and n.nspname = 'pgq'"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_maint, q); db->maint_state = DB_MAINT_TEST_VERSION; } static bool has_ops(PGresult *res) { if (PQntuples(res) == 1 && atoi(PQgetvalue(res, 0, 0)) == 1) return true; return false; } static bool fill_op_list(struct PgDatabase *db, PGresult *res) { int i; struct MaintOp *op = NULL; const char *fname, *farg; free_maint(db); for (i = 0; i < PQntuples(res); i++) { op = calloc(1, sizeof(*op)); if (!op) return false; list_init(&op->head); fname = PQgetvalue(res, i, 0); farg = NULL; if (!PQgetisnull(res, i, 1)) farg = PQgetvalue(res, i, 1); log_debug("load_op: %s / %s", fname, farg ? farg : "NULL"); op->func_name = strdup(fname); if (!op->func_name) goto failed; if (farg) { op->func_arg = strdup(farg); if (!op->func_arg) goto failed; } statlist_append(&db->maint_op_list, &op->head); } return true; failed: free_op(op); return false; } static void run_op_list(struct PgDatabase *db) { const char *q = "select func_name, func_arg from pgq.maint_operations()"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_maint, q); db->maint_state = DB_MAINT_LOAD_OPS; } static const char *stmt_names[] = { "vacuum", "vacuum analyze", NULL }; static void run_op(struct PgDatabase *db, PGresult *res) { struct MaintOp *op; char buf[1024]; char namebuf[256]; const char **np; if (db->cur_maint) { if (res && PQntuples(res) > 0) { const char *val = PQgetvalue(res, 0, 0); if (val && atoi(val)) { op = db->cur_maint; goto repeat; } } next: free_op(db->cur_maint); db->cur_maint = NULL; } op = next_op(db); if (!op) { stats.n_maint++; close_maint(db, cf.maint_period); return; } db->cur_maint = op; repeat: /* check if its magic statement */ for (np = stmt_names; *np; np++) { if (strcasecmp(op->func_name, *np) != 0) continue; if (!pg_quote_fqident(namebuf, op->func_arg, sizeof(namebuf))) { log_error("Bad table name? - %s", op->func_arg); goto next; } /* run as a statement */ snprintf(buf, sizeof(buf), "%s %s", op->func_name, namebuf); log_debug("%s: [%s]", db->name, buf); pgs_send_query_simple(db->c_maint, buf); goto done; } /* run as a function */ if (!pg_quote_fqident(namebuf, op->func_name, sizeof(namebuf))) { log_error("Bad func name? - %s", op->func_name); goto next; } if (op->func_arg) { snprintf(buf, sizeof(buf), "select %s($1)", namebuf); log_debug("%s: [%s]", db->name, buf); pgs_send_query_params(db->c_maint, buf, 1, op->func_arg); } else { snprintf(buf, sizeof(buf), "select %s()", namebuf); log_debug("%s: [%s]", db->name, buf); pgs_send_query_simple(db->c_maint, buf); } done: db->maint_state = DB_MAINT_OP; } static bool fill_items(struct PgDatabase *db, PGresult *res) { int i; if (db->maint_item_list) strlist_free(db->maint_item_list); db->maint_item_list = strlist_new(USUAL_ALLOC); if (!db->maint_item_list) return false; for (i = 0; i < PQntuples(res); i++) { const char *item = PQgetvalue(res, i, 0); if (item) if (!strlist_append(db->maint_item_list, item)) return false; } return true; } static void run_queue_list(struct PgDatabase *db) { const char *q = "select queue_name from pgq.get_queue_info()"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_maint, q); db->maint_state = DB_MAINT_LOAD_QUEUES; } static void run_vacuum_list(struct PgDatabase *db) { const char *q = "select * from pgq.maint_tables_to_vacuum()"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_maint, q); db->maint_state = DB_MAINT_VACUUM_LIST; } static void run_rotate_part1(struct PgDatabase *db) { const char *q; const char *qname; qname = strlist_pop(db->maint_item_list); q = "select pgq.maint_rotate_tables_step1($1)"; log_debug("%s: %s [%s]", db->name, q, qname); pgs_send_query_params(db->c_maint, q, 1, qname); free(qname); db->maint_state = DB_MAINT_ROT1; } static void run_rotate_part2(struct PgDatabase *db) { const char *q = "select pgq.maint_rotate_tables_step2()"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_maint, q); db->maint_state = DB_MAINT_ROT2; } static void run_vacuum(struct PgDatabase *db) { char qbuf[256]; const char *table; table = strlist_pop(db->maint_item_list); snprintf(qbuf, sizeof(qbuf), "vacuum %s", table); log_debug("%s: %s", db->name, qbuf); pgs_send_query_simple(db->c_maint, qbuf); free(table); db->maint_state = DB_MAINT_DO_VACUUM; } static void maint_handler(struct PgSocket *s, void *arg, enum PgEvent ev, PGresult *res) { struct PgDatabase *db = arg; switch (ev) { case PGS_CONNECT_OK: log_debug("%s: starting maintenance", db->name); if (db->has_maint_operations) run_op_list(db); else run_test_version(db); break; case PGS_RESULT_OK: if (PQresultStatus(res) != PGRES_TUPLES_OK) { close_maint(db, 20); return; } switch (db->maint_state) { case DB_MAINT_TEST_VERSION: if (has_ops(res)) { db->has_maint_operations = true; run_op_list(db); } else { run_queue_list(db); } break; case DB_MAINT_LOAD_OPS: if (!fill_op_list(db, res)) goto mem_err; case DB_MAINT_OP: run_op(db, res); break; case DB_MAINT_LOAD_QUEUES: if (!fill_items(db, res)) goto mem_err; case DB_MAINT_ROT1: if (!strlist_empty(db->maint_item_list)) { run_rotate_part1(db); } else { run_rotate_part2(db); } break; case DB_MAINT_ROT2: run_vacuum_list(db); break; case DB_MAINT_VACUUM_LIST: if (!fill_items(db, res)) goto mem_err; case DB_MAINT_DO_VACUUM: if (!strlist_empty(db->maint_item_list)) { run_vacuum(db); } else { close_maint(db, cf.maint_period); } break; default: fatal("bad state"); } break; case PGS_TIMEOUT: log_debug("%s: maint timeout", db->name); if (!pgs_connection_valid(db->c_maint)) launch_maint(db); else run_queue_list(db); break; default: log_warning("%s: default reconnect", db->name); pgs_reconnect(db->c_maint, 60); } return; mem_err: if (db->maint_item_list) { strlist_free(db->maint_item_list); db->maint_item_list = NULL; } pgs_disconnect(db->c_maint); pgs_sleep(db->c_maint, 20); } void launch_maint(struct PgDatabase *db) { const char *cstr; log_debug("%s: launch_maint", db->name); if (!db->c_maint) { if (db->maint_item_list) { strlist_free(db->maint_item_list); db->maint_item_list = NULL; } cstr = make_connstr(db->name); db->c_maint = pgs_create(cstr, maint_handler, db); } if (!pgs_connection_valid(db->c_maint)) { pgs_connect(db->c_maint); } else { /* Already have a connection, what are we doing here */ log_error("%s: maint already initialized", db->name); return; } } pgqd/src/pgqd.c0000664000401600040160000002143113175113043011740 0ustar cbecbe#include "pgqd.h" #include #include #include #include #include #include #include static void detect_dbs(void); static void recheck_dbs(void); static const char usage_str[] = "usage: pgq-ticker [switches] config.file\n" "Switches:\n" " -v Increase verbosity\n" " -q No output to console\n" " -d Daemonize\n" " -h Show help\n" " -V Show version\n" " --ini Show sample config file\n" " -s Stop - send SIGINT to running process\n" " -k Kill - send SIGTERM to running process\n" #ifdef SIGHUP " -r Reload - send SIGHUP to running process\n" #endif ""; static const char *sample_ini = #include "pgqd.ini.h" ; struct Config cf; struct Stats stats; static struct PgSocket *db_template; static STATLIST(database_list); static int got_sigint; #define CF_REL_BASE struct Config static const struct CfKey conf_params[] = { CF_ABS("logfile", CF_FILE, cf_logfile, 0, NULL), CF_REL("pidfile", CF_FILE, pidfile, 0, NULL), CF_REL("initial_database", CF_STR, initial_database, 0, "template1"), CF_REL("base_connstr", CF_STR, base_connstr, 0, ""), CF_REL("database_list", CF_STR, database_list, 0, NULL), CF_ABS("syslog", CF_INT, cf_syslog, 0, "1"), CF_ABS("syslog_ident", CF_STR, cf_syslog_ident, 0, "pgqd"), CF_ABS("syslog_facility", CF_STR, cf_syslog_facility, 0, "daemon"), CF_REL("check_period", CF_TIME_DOUBLE, check_period, 0, "60"), CF_REL("maint_period", CF_TIME_DOUBLE, maint_period, 0, "120"), CF_REL("retry_period", CF_TIME_DOUBLE, retry_period, 0, "30"), CF_REL("ticker_period", CF_TIME_DOUBLE, ticker_period, 0, "1"), CF_REL("stats_period", CF_TIME_DOUBLE, stats_period, 0, "30"), CF_REL("connection_lifetime", CF_TIME_DOUBLE, connection_lifetime, 0, "3600"), { NULL }, }; static const struct CfSect conf_sects[] = { { "pgqd", conf_params }, { NULL } }; static struct CfContext conf_info = { .sect_list = conf_sects, .base = &cf, }; static void load_config(void) { bool ok = cf_load_file(&conf_info, cf.config_file); if (!ok) fatal("failed to read config"); reset_logging(); } static void handle_sigterm(int sock, short flags, void *arg) { log_info("Got SIGTERM, fast exit"); /* pidfile cleanup happens via atexit() */ exit(1); } static void handle_sigint(int sock, short flags, void *arg) { log_info("Got SIGINT, shutting down"); /* notify main loop to exit */ got_sigint = 1; } static void handle_sighup(int sock, short flags, void *arg) { log_info("Got SIGHUP, re-reading config"); load_config(); recheck_dbs(); } static void signal_setup(void) { static struct event ev_sighup; static struct event ev_sigterm; static struct event ev_sigint; int err; #ifdef SIGPIPE sigset_t set; /* block SIGPIPE */ sigemptyset(&set); sigaddset(&set, SIGPIPE); err = sigprocmask(SIG_BLOCK, &set, NULL); if (err < 0) fatal_perror("sigprocmask"); #endif #ifdef SIGHUP /* catch signals */ signal_set(&ev_sighup, SIGHUP, handle_sighup, NULL); err = signal_add(&ev_sighup, NULL); if (err < 0) fatal_perror("signal_add"); #endif signal_set(&ev_sigterm, SIGTERM, handle_sigterm, NULL); err = signal_add(&ev_sigterm, NULL); if (err < 0) fatal_perror("signal_add"); signal_set(&ev_sigint, SIGINT, handle_sigint, NULL); err = signal_add(&ev_sigint, NULL); if (err < 0) fatal_perror("signal_add"); } const char *make_connstr(const char *dbname) { static char buf[512]; snprintf(buf, sizeof(buf), "%s dbname=%s ", cf.base_connstr, dbname); return buf; } static void launch_db(const char *dbname) { struct PgDatabase *db; struct List *elem; /* check of already exists */ statlist_for_each(elem, &database_list) { db = container_of(elem, struct PgDatabase, head); if (strcmp(db->name, dbname) == 0) { db->dropped = false; return; } } /* create new db entry */ db = calloc(1, sizeof(*db)); db->name = strdup(dbname); list_init(&db->head); statlist_init(&db->maint_op_list, "maint_op_list"); statlist_append(&database_list, &db->head); /* start working on it */ launch_ticker(db); } static void drop_db(struct PgDatabase *db, bool log) { if (log) log_info("Unregister database: %s", db->name); statlist_remove(&database_list, &db->head); pgs_free(db->c_ticker); pgs_free(db->c_maint); pgs_free(db->c_retry); free_maint(db); free(db->name); free(db); } static void detect_handler(struct PgSocket *sk, void *arg, enum PgEvent ev, PGresult *res) { int i; const char *s; struct List *el, *tmp; struct PgDatabase *db; switch (ev) { case PGS_CONNECT_OK: pgs_send_query_simple(sk, "select datname from pg_database" " where not datistemplate and datallowconn"); break; case PGS_RESULT_OK: /* tag old dbs as dead */ statlist_for_each(el, &database_list) { db = container_of(el, struct PgDatabase, head); db->dropped = true; } /* process new dbs */ for (i = 0; i < PQntuples(res); i++) { s = PQgetvalue(res, i, 0); launch_db(s); } /* drop old dbs */ statlist_for_each_safe(el, &database_list, tmp) { db = container_of(el, struct PgDatabase, head); if (db->dropped) drop_db(db, true); } pgs_disconnect(sk); pgs_sleep(sk, cf.check_period); break; case PGS_TIMEOUT: detect_dbs(); break; default: pgs_disconnect(sk); pgs_sleep(sk, cf.check_period); } } static void detect_dbs(void) { if (!db_template) { const char *cstr = make_connstr(cf.initial_database); db_template = pgs_create(cstr, detect_handler, NULL); } pgs_connect(db_template); } static bool launch_db_cb(void *arg, const char *db) { launch_db(db); return true; } static void recheck_dbs(void) { struct PgDatabase *db; struct List *el, *tmp; if (cf.database_list && cf.database_list[0]) { /* tag old dbs as dead */ statlist_for_each(el, &database_list) { db = container_of(el, struct PgDatabase, head); db->dropped = true; } /* process new ones */ if (!parse_word_list(cf.database_list, launch_db_cb, NULL)) { log_warning("database_list parsing failed: %s", strerror(errno)); return; } /* drop old ones */ statlist_for_each_safe(el, &database_list, tmp) { db = container_of(el, struct PgDatabase, head); if (db->dropped) drop_db(db, true); } /* done with template for the moment */ if (db_template) { pgs_free(db_template); db_template = NULL; } } else if (!db_template) { log_info("auto-detecting dbs ..."); detect_dbs(); } } static struct event stats_ev; static void stats_handler(int fd, short flags, void *arg) { struct timeval tv = { cf.stats_period, 0 }; log_info("{ticks: %d, maint: %d, retry: %d}", stats.n_ticks, stats.n_maint, stats.n_retry); memset(&stats, 0, sizeof(stats)); if (evtimer_add(&stats_ev, &tv) < 0) fatal_perror("evtimer_add"); } static void stats_setup(void) { struct timeval tv = { cf.stats_period, 0 }; evtimer_set(&stats_ev, stats_handler, NULL); if (evtimer_add(&stats_ev, &tv) < 0) fatal_perror("evtimer_add"); } static void cleanup(void) { struct PgDatabase *db; struct List *elem, *tmp; statlist_for_each_safe(elem, &database_list, tmp) { db = container_of(elem, struct PgDatabase, head); drop_db(db, false); } pgs_free(db_template); event_base_free(NULL); reset_logging(); } static void main_loop_once(void) { reset_time_cache(); if (event_loop(EVLOOP_ONCE) != 0) { log_error("event_loop failed: %s", strerror(errno)); } } int main(int argc, char *argv[]) { int c; bool daemon = false; int sig = 0; const char *signame = NULL; for (c = 1; c < argc; c++) { if (!strcmp(argv[c], "--ini")) { printf("%s", sample_ini); exit(0); } if (!strcmp(argv[c], "--help")) { printf(usage_str); exit(0); } } while ((c = getopt(argc, argv, "dqvhVrsk")) != -1) { switch (c) { case 'd': daemon = true; break; case 'v': cf_verbose++; break; case 'q': cf_quiet = 1; break; case 'h': printf(usage_str); return 0; #ifdef SIGHUP case 'r': sig = SIGHUP; signame = "SIGHUP"; break; #endif case 's': sig = SIGINT; signame = "SIGINT"; break; case 'k': sig = SIGTERM; signame = "SIGTERM"; break; default: printf("bad switch: "); printf(usage_str); return 1; } } if (optind + 1 != argc) { fprintf(stderr, "pgqd requires config file\n"); return 1; } cf.config_file = argv[optind]; load_config(); conf_info.loaded = true; if (sig) { if (!cf.pidfile || !cf.pidfile[0]) { fprintf(stderr, "No pidfile configured\n"); return 1; } if (signal_pidfile(cf.pidfile, sig)) fprintf(stderr, "%s sent\n", signame); else fprintf(stderr, "Old process is not running\n"); return 0; } log_info("Starting pgqd " PACKAGE_VERSION); daemonize(cf.pidfile, daemon); if (!event_init()) fatal("event_init failed"); signal_setup(); stats_setup(); recheck_dbs(); while (!got_sigint) main_loop_once(); cleanup(); return 0; } pgqd/src/retry.c0000664000401600040160000000275713175113043012164 0ustar cbecbe #include "pgqd.h" static void close_retry(struct PgDatabase *db, double sleep_time) { log_debug("%s: close_retry, %f", db->name, sleep_time); pgs_reconnect(db->c_retry, sleep_time); } static void run_retry(struct PgDatabase *db) { const char *q = "select * from pgq.maint_retry_events()"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_retry, q); } static void parse_retry(struct PgDatabase *db, PGresult *res) { if (PQntuples(res) == 1) { char *val = PQgetvalue(res, 0, 0); stats.n_retry += atoi(val); if (strcmp(val, "0") != 0) { run_retry(db); return; } } close_retry(db, cf.retry_period); } static void retry_handler(struct PgSocket *s, void *arg, enum PgEvent ev, PGresult *res) { struct PgDatabase *db = arg; switch (ev) { case PGS_CONNECT_OK: log_debug("%s: starting retry event processing", db->name); run_retry(db); break; case PGS_RESULT_OK: if (PQresultStatus(res) != PGRES_TUPLES_OK) close_retry(db, 20); else parse_retry(db, res); break; case PGS_TIMEOUT: log_debug("%s: retry timeout", db->name); launch_retry(db); break; default: log_warning("%s: default reconnect", db->name); pgs_reconnect(db->c_retry, 30); } } void launch_retry(struct PgDatabase *db) { const char *cstr; if (db->c_retry) { log_debug("%s: retry already initialized", db->name); } else { log_debug("%s: launch_retry", db->name); cstr = make_connstr(db->name); db->c_retry = pgs_create(cstr, retry_handler, db); } pgs_connect(db->c_retry); } pgqd/src/pgqd.h0000664000401600040160000000263513175113043011752 0ustar cbecbe #ifndef __PGQD_H__ #define __PGQD_H__ #include #define Assert(x) #include #include #include #include enum DbState { DB_CLOSED, DB_TICKER_CHECK_PGQ, DB_TICKER_CHECK_VERSION, DB_TICKER_RUN, DB_MAINT_TEST_VERSION, DB_MAINT_LOAD_OPS, DB_MAINT_OP, DB_MAINT_LOAD_QUEUES, DB_MAINT_ROT1, DB_MAINT_ROT2, DB_MAINT_VACUUM_LIST, DB_MAINT_DO_VACUUM, }; struct MaintOp; struct PgDatabase { struct List head; const char *name; struct PgSocket *c_ticker; struct PgSocket *c_maint; struct PgSocket *c_retry; bool has_pgq; enum DbState state; enum DbState maint_state; bool dropped; struct StrList *maint_item_list; struct StatList maint_op_list; struct MaintOp *cur_maint; bool has_maint_operations; }; struct Config { const char *config_file; const char *pidfile; const char *base_connstr; const char *initial_database; const char *database_list; double retry_period; double check_period; double maint_period; double ticker_period; double stats_period; double connection_lifetime; }; struct Stats { int n_ticks; int n_maint; int n_retry; }; extern struct Config cf; extern struct Stats stats; void launch_ticker(struct PgDatabase *db); void launch_maint(struct PgDatabase *db); void launch_retry(struct PgDatabase *db); void free_maint(struct PgDatabase *db); const char *make_connstr(const char *dbname); #endif pgqd/src/ticker.c0000664000401600040160000000640313175113043012270 0ustar cbecbe#include "pgqd.h" static void run_pgq_check(struct PgDatabase *db) { const char *q = "select 1 from pg_catalog.pg_namespace where nspname='pgq'"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_ticker, q); db->state = DB_TICKER_CHECK_PGQ; } static void run_version_check(struct PgDatabase *db) { const char *q = "select pgq.version()"; log_debug("%s: %s", db->name, q); pgs_send_query_simple(db->c_ticker, q); db->state = DB_TICKER_CHECK_VERSION; } static void run_ticker(struct PgDatabase *db) { const char *q = "select pgq.ticker()"; log_noise("%s: %s", db->name, q); pgs_send_query_simple(db->c_ticker, q); db->state = DB_TICKER_RUN; } static void close_ticker(struct PgDatabase *db, double sleep_time) { log_debug("%s: close_ticker, %f", db->name, sleep_time); db->state = DB_CLOSED; pgs_reconnect(db->c_ticker, sleep_time); } static void parse_pgq_check(struct PgDatabase *db, PGresult *res) { db->has_pgq = PQntuples(res) == 1; if (!db->has_pgq) { log_debug("%s: no pgq", db->name); close_ticker(db, cf.check_period); } else { run_version_check(db); } } static void parse_version_check(struct PgDatabase *db, PGresult *res) { char *ver; if (PQntuples(res) != 1) { log_debug("%s: calling pgq.version() failed", db->name); goto badpgq; } ver = PQgetvalue(res, 0, 0); if (ver[0] < '3') { log_debug("%s: bad pgq version: %s", db->name, ver); goto badpgq; } log_info("%s: pgq version ok: %s", db->name, ver); run_ticker(db); if (!db->c_maint) launch_maint(db); if (!db->c_retry) launch_retry(db); return; badpgq: db->has_pgq = false; log_info("%s: bad pgq version, ignoring", db->name); close_ticker(db, cf.check_period); } static void parse_ticker_result(struct PgDatabase *db, PGresult *res) { if (PQntuples(res) != 1) { log_debug("%s: calling pgq.ticker() failed", db->name); } else { stats.n_ticks++; } pgs_sleep(db->c_ticker, cf.ticker_period); } static void tick_handler(struct PgSocket *s, void *arg, enum PgEvent ev, PGresult *res) { struct PgDatabase *db = arg; ExecStatusType st; switch (ev) { case PGS_CONNECT_OK: run_pgq_check(db); break; case PGS_RESULT_OK: if (PQresultStatus(res) != PGRES_TUPLES_OK) { close_ticker(db, 10); break; } switch (db->state) { case DB_TICKER_CHECK_PGQ: parse_pgq_check(db, res); break; case DB_TICKER_CHECK_VERSION: parse_version_check(db, res); break; case DB_TICKER_RUN: parse_ticker_result(db, res); break; case DB_CLOSED: st = PQresultStatus(res); log_warning("%s: Weird state: RESULT_OK + DB_CLOSED (%s)", db->name, PQresStatus(st)); close_ticker(db, 10); break; default: log_warning("%s: bad state: %d", db->name, db->state); close_ticker(db, 10); } break; case PGS_TIMEOUT: log_noise("%s: tick timeout", db->name); if (!pgs_connection_valid(db->c_ticker)) launch_ticker(db); else run_ticker(db); break; default: log_warning("%s: default timeout", db->name); pgs_reconnect(db->c_ticker, 60); } } void launch_ticker(struct PgDatabase *db) { log_debug("%s: launch_ticker", db->name); if (!db->c_ticker) { const char *cstr = make_connstr(db->name); db->c_ticker = pgs_create(cstr, tick_handler, db); pgs_set_lifetime(db->c_ticker, cf.connection_lifetime); } pgs_connect(db->c_ticker); }