aboutsummaryrefslogtreecommitdiffstats
path: root/lib/base
diff options
context:
space:
mode:
authorHiroki Sato <hrs@FreeBSD.org>2018-04-04 04:21:19 +0000
committerHiroki Sato <hrs@FreeBSD.org>2018-04-04 04:21:19 +0000
commitd684f11da759490a8d98d7b790796106285f4084 (patch)
tree27b7356df710fdf1440fe2c23154b8121e99f2ab /lib/base
parentf52d4664e3f68828c06f85bfc1afa271e3e04713 (diff)
downloadsrc-d684f11da759490a8d98d7b790796106285f4084.tar.gz
src-d684f11da759490a8d98d7b790796106285f4084.zip
Notes
Notes: svn path=/vendor-crypto/heimdal/dist/; revision=331978 svn path=/vendor-crypto/heimdal/7.5.0/; revision=331979; tag=vendor/heimdal/7.5.0
Diffstat (limited to 'lib/base')
-rw-r--r--lib/base/Makefile.am64
-rw-r--r--lib/base/Makefile.in1482
-rw-r--r--lib/base/NTMakefile82
-rw-r--r--lib/base/array.c478
-rw-r--r--lib/base/baselocl.h167
-rw-r--r--lib/base/bool.c59
-rw-r--r--lib/base/bsearch.c886
-rw-r--r--lib/base/data.c165
-rw-r--r--lib/base/db.c1727
-rw-r--r--lib/base/dict.c303
-rw-r--r--lib/base/dll.c324
-rw-r--r--lib/base/error.c178
-rw-r--r--lib/base/heimbase.c1079
-rw-r--r--lib/base/heimbase.h431
-rw-r--r--lib/base/heimbasepriv.h113
-rw-r--r--lib/base/heimqueue.h167
-rw-r--r--lib/base/json.c811
-rw-r--r--lib/base/null.c53
-rw-r--r--lib/base/number.c128
-rw-r--r--lib/base/roken_rename.h61
-rw-r--r--lib/base/string.c260
-rw-r--r--lib/base/test_base.c961
-rw-r--r--lib/base/version-script.map94
23 files changed, 10073 insertions, 0 deletions
diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am
new file mode 100644
index 000000000000..ee5f120d3f5d
--- /dev/null
+++ b/lib/base/Makefile.am
@@ -0,0 +1,64 @@
+
+include $(top_srcdir)/Makefile.am.common
+
+if do_roken_rename
+ES = base64.c
+endif
+
+IMPLEMENT_TLS=
+if MAINTAINER_MODE
+IMPLEMENT_TLS += dll.c
+AM_CPPFLAGS += -DHEIM_BASE_MAINTAINER
+endif
+
+AM_CPPFLAGS += $(ROKEN_RENAME)
+
+lib_LTLIBRARIES = libheimbase.la
+check_PROGRAMS = test_base
+
+libheimbase_la_LDFLAGS = -version-info 1:0:0
+
+TESTS = test_base
+
+if versionscript
+libheimbase_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+endif
+
+libheimbase_la_LIBADD = $(PTHREAD_LIBADD)
+
+include_HEADERS = heimbase.h
+
+dist_libheimbase_la_SOURCES = \
+ array.c \
+ baselocl.h \
+ bsearch.c \
+ bool.c \
+ data.c \
+ db.c \
+ dict.c \
+ $(IMPLEMENT_TLS) \
+ error.c \
+ heimbase.c \
+ heimbasepriv.h \
+ heimqueue.h \
+ json.c \
+ null.c \
+ number.c \
+ roken_rename.h \
+ string.c
+
+nodist_libheimbase_la_SOURCES = $(ES)
+
+# install these?
+
+libheimbase_la_DEPENDENCIES = version-script.map
+
+test_base_LDADD = libheimbase.la $(LIB_roken)
+
+CLEANFILES = base64.c test_db.json
+
+EXTRA_DIST = NTMakefile version-script.map
+
+base64.c:
+ rm -f base64.c
+ $(LN_S) $(srcdir)/../roken/base64.c .
diff --git a/lib/base/Makefile.in b/lib/base/Makefile.in
new file mode 100644
index 000000000000..9c147666e795
--- /dev/null
+++ b/lib/base/Makefile.in
@@ -0,0 +1,1482 @@
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $Id$
+
+# $Id$
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@MAINTAINER_MODE_TRUE@am__append_1 = dll.c
+@MAINTAINER_MODE_TRUE@am__append_2 = -DHEIM_BASE_MAINTAINER
+check_PROGRAMS = test_base$(EXEEXT)
+TESTS = test_base$(EXEEXT)
+@versionscript_TRUE@am__append_3 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+subdir = lib/base
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
+ $(top_srcdir)/cf/auth-modules.m4 \
+ $(top_srcdir)/cf/broken-getaddrinfo.m4 \
+ $(top_srcdir)/cf/broken-glob.m4 \
+ $(top_srcdir)/cf/broken-realloc.m4 \
+ $(top_srcdir)/cf/broken-snprintf.m4 $(top_srcdir)/cf/broken.m4 \
+ $(top_srcdir)/cf/broken2.m4 $(top_srcdir)/cf/c-attribute.m4 \
+ $(top_srcdir)/cf/capabilities.m4 \
+ $(top_srcdir)/cf/check-compile-et.m4 \
+ $(top_srcdir)/cf/check-getpwnam_r-posix.m4 \
+ $(top_srcdir)/cf/check-man.m4 \
+ $(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
+ $(top_srcdir)/cf/check-type-extra.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
+ $(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
+ $(top_srcdir)/cf/find-func-no-libs.m4 \
+ $(top_srcdir)/cf/find-func-no-libs2.m4 \
+ $(top_srcdir)/cf/find-func.m4 \
+ $(top_srcdir)/cf/find-if-not-broken.m4 \
+ $(top_srcdir)/cf/framework-security.m4 \
+ $(top_srcdir)/cf/have-struct-field.m4 \
+ $(top_srcdir)/cf/have-type.m4 $(top_srcdir)/cf/irix.m4 \
+ $(top_srcdir)/cf/krb-bigendian.m4 \
+ $(top_srcdir)/cf/krb-func-getlogin.m4 \
+ $(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
+ $(top_srcdir)/cf/krb-readline.m4 \
+ $(top_srcdir)/cf/krb-struct-spwd.m4 \
+ $(top_srcdir)/cf/krb-struct-winsize.m4 \
+ $(top_srcdir)/cf/largefile.m4 $(top_srcdir)/cf/libtool.m4 \
+ $(top_srcdir)/cf/ltoptions.m4 $(top_srcdir)/cf/ltsugar.m4 \
+ $(top_srcdir)/cf/ltversion.m4 $(top_srcdir)/cf/lt~obsolete.m4 \
+ $(top_srcdir)/cf/mips-abi.m4 $(top_srcdir)/cf/misc.m4 \
+ $(top_srcdir)/cf/need-proto.m4 $(top_srcdir)/cf/osfc2.m4 \
+ $(top_srcdir)/cf/otp.m4 $(top_srcdir)/cf/pkg.m4 \
+ $(top_srcdir)/cf/proto-compat.m4 $(top_srcdir)/cf/pthreads.m4 \
+ $(top_srcdir)/cf/resolv.m4 $(top_srcdir)/cf/retsigtype.m4 \
+ $(top_srcdir)/cf/roken-frag.m4 \
+ $(top_srcdir)/cf/socket-wrapper.m4 $(top_srcdir)/cf/sunos.m4 \
+ $(top_srcdir)/cf/telnet.m4 $(top_srcdir)/cf/test-package.m4 \
+ $(top_srcdir)/cf/version-script.m4 $(top_srcdir)/cf/wflags.m4 \
+ $(top_srcdir)/cf/win32.m4 $(top_srcdir)/cf/with-all.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+am__dist_libheimbase_la_SOURCES_DIST = array.c baselocl.h bsearch.c \
+ bool.c data.c db.c dict.c dll.c error.c heimbase.c \
+ heimbasepriv.h heimqueue.h json.c null.c number.c \
+ roken_rename.h string.c
+@MAINTAINER_MODE_TRUE@am__objects_1 = dll.lo
+am__objects_2 = $(am__objects_1)
+dist_libheimbase_la_OBJECTS = array.lo bsearch.lo bool.lo data.lo \
+ db.lo dict.lo $(am__objects_2) error.lo heimbase.lo json.lo \
+ null.lo number.lo string.lo
+@do_roken_rename_TRUE@am__objects_3 = base64.lo
+nodist_libheimbase_la_OBJECTS = $(am__objects_3)
+libheimbase_la_OBJECTS = $(dist_libheimbase_la_OBJECTS) \
+ $(nodist_libheimbase_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libheimbase_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libheimbase_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+test_base_SOURCES = test_base.c
+test_base_OBJECTS = test_base.$(OBJEXT)
+test_base_DEPENDENCIES = libheimbase.la $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(dist_libheimbase_la_SOURCES) \
+ $(nodist_libheimbase_la_SOURCES) test_base.c
+DIST_SOURCES = $(am__dist_libheimbase_la_SOURCES_DIST) test_base.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+ASN1_COMPILE = @ASN1_COMPILE@
+ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CANONICAL_HOST = @CANONICAL_HOST@
+CAPNG_CFLAGS = @CAPNG_CFLAGS@
+CAPNG_LIBS = @CAPNG_LIBS@
+CATMAN = @CATMAN@
+CATMANEXT = @CATMANEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COMPILE_ET = @COMPILE_ET@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
+DBHEADER = @DBHEADER@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DIR_com_err = @DIR_com_err@
+DIR_hdbdir = @DIR_hdbdir@
+DIR_roken = @DIR_roken@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
+GREP = @GREP@
+GROFF = @GROFF@
+INCLUDES_roken = @INCLUDES_roken@
+INCLUDE_libedit = @INCLUDE_libedit@
+INCLUDE_libintl = @INCLUDE_libintl@
+INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
+INCLUDE_readline = @INCLUDE_readline@
+INCLUDE_sqlite3 = @INCLUDE_sqlite3@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_VERSION_SCRIPT = @LDFLAGS_VERSION_SCRIPT@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBADD_roken = @LIBADD_roken@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
+LIB_bswap16 = @LIB_bswap16@
+LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
+LIB_com_err = @LIB_com_err@
+LIB_com_err_a = @LIB_com_err_a@
+LIB_com_err_so = @LIB_com_err_so@
+LIB_crypt = @LIB_crypt@
+LIB_db_create = @LIB_db_create@
+LIB_dbm_firstkey = @LIB_dbm_firstkey@
+LIB_dbopen = @LIB_dbopen@
+LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
+LIB_dlopen = @LIB_dlopen@
+LIB_dn_expand = @LIB_dn_expand@
+LIB_dns_search = @LIB_dns_search@
+LIB_door_create = @LIB_door_create@
+LIB_freeaddrinfo = @LIB_freeaddrinfo@
+LIB_gai_strerror = @LIB_gai_strerror@
+LIB_getaddrinfo = @LIB_getaddrinfo@
+LIB_gethostbyname = @LIB_gethostbyname@
+LIB_gethostbyname2 = @LIB_gethostbyname2@
+LIB_getnameinfo = @LIB_getnameinfo@
+LIB_getpwnam_r = @LIB_getpwnam_r@
+LIB_getsockopt = @LIB_getsockopt@
+LIB_hcrypto = @LIB_hcrypto@
+LIB_hcrypto_a = @LIB_hcrypto_a@
+LIB_hcrypto_appl = @LIB_hcrypto_appl@
+LIB_hcrypto_so = @LIB_hcrypto_so@
+LIB_hstrerror = @LIB_hstrerror@
+LIB_kdb = @LIB_kdb@
+LIB_libedit = @LIB_libedit@
+LIB_libintl = @LIB_libintl@
+LIB_loadquery = @LIB_loadquery@
+LIB_logout = @LIB_logout@
+LIB_logwtmp = @LIB_logwtmp@
+LIB_openldap = @LIB_openldap@
+LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
+LIB_otp = @LIB_otp@
+LIB_pidfile = @LIB_pidfile@
+LIB_readline = @LIB_readline@
+LIB_res_ndestroy = @LIB_res_ndestroy@
+LIB_res_nsearch = @LIB_res_nsearch@
+LIB_res_search = @LIB_res_search@
+LIB_roken = @LIB_roken@
+LIB_security = @LIB_security@
+LIB_setsockopt = @LIB_setsockopt@
+LIB_socket = @LIB_socket@
+LIB_sqlite3 = @LIB_sqlite3@
+LIB_syslog = @LIB_syslog@
+LIB_tgetent = @LIB_tgetent@
+LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NO_AFS = @NO_AFS@
+NROFF = @NROFF@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LDADD = @PTHREAD_LDADD@
+PTHREAD_LIBADD = @PTHREAD_LIBADD@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SLC = @SLC@
+SLC_DEP = @SLC_DEP@
+STRIP = @STRIP@
+VERSION = @VERSION@
+VERSIONING = @VERSIONING@
+WFLAGS = @WFLAGS@
+WFLAGS_LITE = @WFLAGS_LITE@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
+docdir = @docdir@
+dpagaix_cflags = @dpagaix_cflags@
+dpagaix_ldadd = @dpagaix_ldadd@
+dpagaix_ldflags = @dpagaix_ldflags@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
+AM_CPPFLAGS = $(INCLUDES_roken) $(am__append_2) $(ROKEN_RENAME)
+@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
+AM_CFLAGS = $(WFLAGS)
+CP = cp
+buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
+LIB_el_init = @LIB_el_init@
+LIB_getattr = @LIB_getattr@
+LIB_getpwent_r = @LIB_getpwent_r@
+LIB_odm_initialize = @LIB_odm_initialize@
+LIB_setpcred = @LIB_setpcred@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
+libexec_heimdaldir = $(libexecdir)/heimdal
+NROFF_MAN = groff -mandoc -Tascii
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
+@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
+@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
+
+@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
+@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
+@do_roken_rename_TRUE@ES = base64.c
+IMPLEMENT_TLS = $(am__append_1)
+lib_LTLIBRARIES = libheimbase.la
+libheimbase_la_LDFLAGS = -version-info 1:0:0 $(am__append_3)
+libheimbase_la_LIBADD = $(PTHREAD_LIBADD)
+include_HEADERS = heimbase.h
+dist_libheimbase_la_SOURCES = \
+ array.c \
+ baselocl.h \
+ bsearch.c \
+ bool.c \
+ data.c \
+ db.c \
+ dict.c \
+ $(IMPLEMENT_TLS) \
+ error.c \
+ heimbase.c \
+ heimbasepriv.h \
+ heimqueue.h \
+ json.c \
+ null.c \
+ number.c \
+ roken_rename.h \
+ string.c
+
+nodist_libheimbase_la_SOURCES = $(ES)
+
+# install these?
+libheimbase_la_DEPENDENCIES = version-script.map
+test_base_LDADD = libheimbase.la $(LIB_roken)
+CLEANFILES = base64.c test_db.json
+EXTRA_DIST = NTMakefile version-script.map
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/base/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign lib/base/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libheimbase.la: $(libheimbase_la_OBJECTS) $(libheimbase_la_DEPENDENCIES) $(EXTRA_libheimbase_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libheimbase_la_LINK) -rpath $(libdir) $(libheimbase_la_OBJECTS) $(libheimbase_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+test_base$(EXEEXT): $(test_base_OBJECTS) $(test_base_DEPENDENCIES) $(EXTRA_test_base_DEPENDENCIES)
+ @rm -f test_base$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_base_OBJECTS) $(test_base_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bool.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bsearch.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heimbase.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/null.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/number.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_base.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+ fi; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test_base.log: test_base$(EXEEXT)
+ @p='test_base$(EXEEXT)'; \
+ b='test_base'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS) all-local
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-data-hook
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-includeHEADERS \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+ uninstall uninstall-am uninstall-hook uninstall-includeHEADERS \
+ uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+install-suid-programs:
+ @foo='$(bin_SUIDS)'; \
+ for file in $$foo; do \
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
+ for f in $$foo; do \
+ f=`basename $$f`; \
+ if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
+ else file="$$f"; fi; \
+ if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
+ : ; else \
+ echo " $(CP) $$file $(buildinclude)/$$f"; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
+ fi ; \
+ done ; \
+ foo='$(nobase_include_HEADERS)'; \
+ for f in $$foo; do \
+ if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
+ else file="$$f"; fi; \
+ $(mkdir_p) $(buildinclude)/`dirname $$f` ; \
+ if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
+ : ; else \
+ echo " $(CP) $$file $(buildinclude)/$$f"; \
+ $(CP) $$file $(buildinclude)/$$f; \
+ fi ; \
+ done
+
+all-local: install-build-headers
+
+check-local::
+ @if test '$(CHECK_LOCAL)' = "no-check-local"; then \
+ foo=''; elif test '$(CHECK_LOCAL)'; then \
+ foo='$(CHECK_LOCAL)'; else \
+ foo='$(PROGRAMS)'; fi; \
+ if test "$$foo"; then \
+ failed=0; all=0; \
+ for i in $$foo; do \
+ all=`expr $$all + 1`; \
+ if (./$$i --version && ./$$i --help) > /dev/null 2>&1; then \
+ echo "PASS: $$i"; \
+ else \
+ echo "FAIL: $$i"; \
+ failed=`expr $$failed + 1`; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="$$failed of $$all tests failed"; \
+ fi; \
+ dashes=`echo "$$banner" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0 || exit 1; \
+ fi
+
+.x.c:
+ @cmp -s $< $@ 2> /dev/null || cp $< $@
+
+.hx.h:
+ @cmp -s $< $@ 2> /dev/null || cp $< $@
+#NROFF_MAN = nroff -man
+.1.cat1:
+ $(NROFF_MAN) $< > $@
+.3.cat3:
+ $(NROFF_MAN) $< > $@
+.5.cat5:
+ $(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
+.8.cat8:
+ $(NROFF_MAN) $< > $@
+
+dist-cat1-mans:
+ @foo='$(man1_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.1) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat1/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat3-mans:
+ @foo='$(man3_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.3) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat3/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat5-mans:
+ @foo='$(man5_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.5) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat5/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat8-mans:
+ @foo='$(man8_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.8) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat8/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
+
+install-cat-mans:
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
+
+uninstall-cat-mans:
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
+
+install-data-hook: install-cat-mans
+uninstall-hook: uninstall-cat-mans
+
+.et.h:
+ $(COMPILE_ET) $<
+.et.c:
+ $(COMPILE_ET) $<
+
+#
+# Useful target for debugging
+#
+
+check-valgrind:
+ tobjdir=`cd $(top_builddir) && pwd` ; \
+ tsrcdir=`cd $(top_srcdir) && pwd` ; \
+ env TESTS_ENVIRONMENT="$${tsrcdir}/cf/maybe-valgrind.sh -s $${tsrcdir} -o $${tobjdir}" make check
+
+#
+# Target to please samba build farm, builds distfiles in-tree.
+# Will break when automake changes...
+#
+
+distdir-in-tree: $(DISTFILES) $(INFO_DEPS)
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" != .; then \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) distdir-in-tree) ; \
+ fi ; \
+ done
+
+base64.c:
+ rm -f base64.c
+ $(LN_S) $(srcdir)/../roken/base64.c .
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/base/NTMakefile b/lib/base/NTMakefile
new file mode 100644
index 000000000000..e5bda31dd722
--- /dev/null
+++ b/lib/base/NTMakefile
@@ -0,0 +1,82 @@
+########################################################################
+#
+# Copyright (c) 2010, Secure Endpoints Inc.
+# All rights reserved.
+#
+# 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 HOLDER 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.
+#
+
+RELDIR=lib\base
+
+intcflags=-I$(SRCDIR) -I$(OBJ)
+
+!include ../../windows/NTMakefile.w32
+
+INCFILES=$(INCDIR)\heimbase.h
+
+test_binaries = $(OBJ)\test_base.exe
+
+libheimbase_OBJS = \
+ $(OBJ)\array.obj \
+ $(OBJ)\bool.obj \
+ $(OBJ)\bsearch.obj \
+ $(OBJ)\data.obj \
+ $(OBJ)\db.obj \
+ $(OBJ)\dict.obj \
+ $(OBJ)\dll.obj \
+ $(OBJ)\error.obj \
+ $(OBJ)\heimbase.obj \
+ $(OBJ)\json.obj \
+ $(OBJ)\null.obj \
+ $(OBJ)\number.obj \
+ $(OBJ)\string.obj
+
+$(LIBHEIMBASE): $(libheimbase_OBJS)
+ $(LIBCON_C) -OUT:$@ $(LIBROKEN) @<<
+$(libheimbase_OBJS: =
+)
+<<
+
+test:: test-binaries test-run
+
+test-run:
+ cd $(OBJ)
+ -test_base.exe
+ cd $(SRCDIR)
+
+all:: $(INCFILES) $(LIBHEIMBASE)
+
+clean::
+ -$(RM) $(INCFILES)
+
+test-binaries: $(test_binaries)
+
+$(test_binaries): $$(@R).obj $(LIBHEIMBASE) $(LIBVERS) $(LIBROKEN)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
+$(test_binaries:.exe=.obj): $$(@B).c
+ $(C2OBJ_C) -Fo$@ -Fd$(@D)\ $** -DBlah
diff --git a/lib/base/array.c b/lib/base/array.c
new file mode 100644
index 000000000000..b34f9de48800
--- /dev/null
+++ b/lib/base/array.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+
+/*
+ *
+ */
+
+struct heim_array_data {
+ size_t len;
+ heim_object_t *val;
+ size_t allocated_len;
+ heim_object_t *allocated;
+};
+
+static void
+array_dealloc(heim_object_t ptr)
+{
+ heim_array_t array = ptr;
+ size_t n;
+ for (n = 0; n < array->len; n++)
+ heim_release(array->val[n]);
+ free(array->allocated);
+}
+
+struct heim_type_data array_object = {
+ HEIM_TID_ARRAY,
+ "dict-object",
+ NULL,
+ array_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/**
+ * Allocate an array
+ *
+ * @return A new allocated array, free with heim_release()
+ */
+
+heim_array_t
+heim_array_create(void)
+{
+ heim_array_t array;
+
+ array = _heim_alloc_object(&array_object, sizeof(*array));
+ if (array == NULL)
+ return NULL;
+
+ array->allocated = NULL;
+ array->allocated_len = 0;
+ array->val = NULL;
+ array->len = 0;
+
+ return array;
+}
+
+/**
+ * Get type id of an dict
+ *
+ * @return the type id
+ */
+
+heim_tid_t
+heim_array_get_type_id(void)
+{
+ return HEIM_TID_ARRAY;
+}
+
+/**
+ * Append object to array
+ *
+ * @param array array to add too
+ * @param object the object to add
+ *
+ * @return zero if added, errno otherwise
+ */
+
+int
+heim_array_append_value(heim_array_t array, heim_object_t object)
+{
+ heim_object_t *ptr;
+ size_t leading = array->val - array->allocated; /* unused leading slots */
+ size_t trailing = array->allocated_len - array->len - leading;
+ size_t new_len;
+
+ if (trailing > 0) {
+ /* We have pre-allocated space; use it */
+ array->val[array->len++] = heim_retain(object);
+ return 0;
+ }
+
+ if (leading > (array->len + 1)) {
+ /*
+ * We must have appending to, and deleting at index 0 from this
+ * array a lot; don't want to grow forever!
+ */
+ (void) memmove(&array->allocated[0], &array->val[0],
+ array->len * sizeof(array->val[0]));
+ array->val = array->allocated;
+
+ /* We have pre-allocated space; use it */
+ array->val[array->len++] = heim_retain(object);
+ return 0;
+ }
+
+ /* Pre-allocate extra .5 times number of used slots */
+ new_len = leading + array->len + 1 + (array->len >> 1);
+ ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
+ if (ptr == NULL)
+ return ENOMEM;
+ array->allocated = ptr;
+ array->allocated_len = new_len;
+ array->val = &ptr[leading];
+ array->val[array->len++] = heim_retain(object);
+
+ return 0;
+}
+
+/*
+ * Internal function to insert at index 0, taking care to optimize the
+ * case where we're always inserting at index 0, particularly the case
+ * where we insert at index 0 and delete from the right end.
+ */
+static int
+heim_array_prepend_value(heim_array_t array, heim_object_t object)
+{
+ heim_object_t *ptr;
+ size_t leading = array->val - array->allocated; /* unused leading slots */
+ size_t trailing = array->allocated_len - array->len - leading;
+ size_t new_len;
+
+ if (leading > 0) {
+ /* We have pre-allocated space; use it */
+ array->val--;
+ array->val[0] = heim_retain(object);
+ array->len++;
+ return 0;
+ }
+ if (trailing > (array->len + 1)) {
+ /*
+ * We must have prepending to, and deleting at index
+ * array->len - 1 from this array a lot; don't want to grow
+ * forever!
+ */
+ (void) memmove(&array->allocated[array->len], &array->val[0],
+ array->len * sizeof(array->val[0]));
+ array->val = &array->allocated[array->len];
+
+ /* We have pre-allocated space; use it */
+ array->val--;
+ array->val[0] = heim_retain(object);
+ array->len++;
+ return 0;
+ }
+ /* Pre-allocate extra .5 times number of used slots */
+ new_len = array->len + 1 + trailing + (array->len >> 1);
+ ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
+ if (ptr == NULL)
+ return ENOMEM;
+ (void) memmove(&ptr[1], &ptr[0], array->len * sizeof (array->val[0]));
+ array->allocated = ptr;
+ array->allocated_len = new_len;
+ array->val = &ptr[0];
+ array->val[0] = heim_retain(object);
+ array->len++;
+
+ return 0;
+}
+
+/**
+ * Insert an object at a given index in an array
+ *
+ * @param array array to add too
+ * @param idx index where to add element (-1 == append, -2 next to last, ...)
+ * @param object the object to add
+ *
+ * @return zero if added, errno otherwise
+ */
+
+int
+heim_array_insert_value(heim_array_t array, size_t idx, heim_object_t object)
+{
+ int ret;
+
+ if (idx == 0)
+ return heim_array_prepend_value(array, object);
+ else if (idx > array->len)
+ heim_abort("index too large");
+
+ /*
+ * We cheat: append this element then rotate elements around so we
+ * have this new element at the desired location, unless we're truly
+ * appending the new element. This means reusing array growth in
+ * heim_array_append_value() instead of duplicating that here.
+ */
+ ret = heim_array_append_value(array, object);
+ if (ret != 0 || idx == (array->len - 1))
+ return ret;
+ /*
+ * Shift to the right by one all the elements after idx, then set
+ * [idx] to the new object.
+ */
+ (void) memmove(&array->val[idx + 1], &array->val[idx],
+ (array->len - idx - 1) * sizeof(array->val[0]));
+ array->val[idx] = heim_retain(object);
+
+ return 0;
+}
+
+/**
+ * Iterate over all objects in array
+ *
+ * @param array array to iterate over
+ * @param ctx context passed to fn
+ * @param fn function to call on each object
+ */
+
+void
+heim_array_iterate_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
+{
+ size_t n;
+ int stop = 0;
+ for (n = 0; n < array->len; n++) {
+ fn(array->val[n], ctx, &stop);
+ if (stop)
+ return;
+ }
+}
+
+#ifdef __BLOCKS__
+/**
+ * Iterate over all objects in array
+ *
+ * @param array array to iterate over
+ * @param fn block to call on each object
+ */
+
+void
+heim_array_iterate(heim_array_t array, void (^fn)(heim_object_t, int *))
+{
+ size_t n;
+ int stop = 0;
+ for (n = 0; n < array->len; n++) {
+ fn(array->val[n], &stop);
+ if (stop)
+ return;
+ }
+}
+#endif
+
+/**
+ * Iterate over all objects in array, backwards
+ *
+ * @param array array to iterate over
+ * @param ctx context passed to fn
+ * @param fn function to call on each object
+ */
+
+void
+heim_array_iterate_reverse_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
+{
+ size_t n;
+ int stop = 0;
+
+ for (n = array->len; n > 0; n--) {
+ fn(array->val[n - 1], ctx, &stop);
+ if (stop)
+ return;
+ }
+}
+
+#ifdef __BLOCKS__
+/**
+ * Iterate over all objects in array, backwards
+ *
+ * @param array array to iterate over
+ * @param fn block to call on each object
+ */
+
+void
+heim_array_iterate_reverse(heim_array_t array, void (^fn)(heim_object_t, int *))
+{
+ size_t n;
+ int stop = 0;
+ for (n = array->len; n > 0; n--) {
+ fn(array->val[n - 1], &stop);
+ if (stop)
+ return;
+ }
+}
+#endif
+
+/**
+ * Get length of array
+ *
+ * @param array array to get length of
+ *
+ * @return length of array
+ */
+
+size_t
+heim_array_get_length(heim_array_t array)
+{
+ return array->len;
+}
+
+/**
+ * Get value of element at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ *
+ * @return a not-retained copy of the object
+ */
+
+heim_object_t
+heim_array_get_value(heim_array_t array, size_t idx)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ return array->val[idx];
+}
+
+/**
+ * Get value of element at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ *
+ * @return a retained copy of the object
+ */
+
+heim_object_t
+heim_array_copy_value(heim_array_t array, size_t idx)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ return heim_retain(array->val[idx]);
+}
+
+/**
+ * Set value at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ * @param value value to set
+ *
+ */
+
+void
+heim_array_set_value(heim_array_t array, size_t idx, heim_object_t value)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ heim_release(array->val[idx]);
+ array->val[idx] = heim_retain(value);
+}
+
+/**
+ * Delete value at idx
+ *
+ * @param array the array to modify
+ * @param idx the key to delete
+ */
+
+void
+heim_array_delete_value(heim_array_t array, size_t idx)
+{
+ heim_object_t obj;
+ if (idx >= array->len)
+ heim_abort("index too large");
+ obj = array->val[idx];
+
+ array->len--;
+
+ /*
+ * Deleting the first or last elements is cheap, as we leave
+ * allocated space for opportunistic reuse later; no realloc(), no
+ * memmove(). All others require a memmove().
+ *
+ * If we ever need to optimize deletion of non-last/ non-first
+ * element we can use a tagged object type to signify "deleted
+ * value" so we can leave holes in the array, avoid memmove()s on
+ * delete, and opportunistically re-use those holes on insert.
+ */
+ if (idx == 0)
+ array->val++;
+ else if (idx < array->len)
+ (void) memmove(&array->val[idx], &array->val[idx + 1],
+ (array->len - idx) * sizeof(array->val[0]));
+
+ heim_release(obj);
+}
+
+/**
+ * Filter out entres of array when function return true
+ *
+ * @param array the array to modify
+ * @param fn filter function
+ */
+
+void
+heim_array_filter_f(heim_array_t array, void *ctx, heim_array_filter_f_t fn)
+{
+ size_t n = 0;
+
+ while (n < array->len) {
+ if (fn(array->val[n], ctx)) {
+ heim_array_delete_value(array, n);
+ } else {
+ n++;
+ }
+ }
+}
+
+#ifdef __BLOCKS__
+
+/**
+ * Filter out entres of array when block return true
+ *
+ * @param array the array to modify
+ * @param block filter block
+ */
+
+void
+heim_array_filter(heim_array_t array, int (^block)(heim_object_t))
+{
+ size_t n = 0;
+
+ while (n < array->len) {
+ if (block(array->val[n])) {
+ heim_array_delete_value(array, n);
+ } else {
+ n++;
+ }
+ }
+}
+
+#endif /* __BLOCKS__ */
diff --git a/lib/base/baselocl.h b/lib/base/baselocl.h
new file mode 100644
index 000000000000..b24c13d4fb24
--- /dev/null
+++ b/lib/base/baselocl.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010 - 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "config.h"
+
+#include <roken.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define HEIMDAL_TEXTDOMAIN "heimdal_krb5"
+
+#ifdef LIBINTL
+#include <libintl.h>
+#define N_(x,y) dgettext(HEIMDAL_TEXTDOMAIN, x)
+#else
+#define N_(x,y) (x)
+#define bindtextdomain(package, localedir)
+#endif
+
+#include "heimqueue.h"
+#include "heim_threads.h"
+#include "heimbase.h"
+#include "heimbasepriv.h"
+
+#ifdef HAVE_DISPATCH_DISPATCH_H
+#include <dispatch/dispatch.h>
+#endif
+
+#if defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH)
+
+#define heim_base_atomic_inc(x) __sync_add_and_fetch((x), 1)
+#define heim_base_atomic_dec(x) __sync_sub_and_fetch((x), 1)
+#define heim_base_atomic_type unsigned int
+#define heim_base_atomic_max UINT_MAX
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if __has_builtin(__sync_swap)
+#define heim_base_exchange_pointer(t,v) __sync_swap((t), (v))
+#else
+#define heim_base_exchange_pointer(t,v) __sync_lock_test_and_set((t), (v))
+#endif
+
+#elif defined(__sun)
+
+#include <sys/atomic.h>
+
+#define heim_base_atomic_inc(x) atomic_inc_uint_nv((volatile uint_t *)(x))
+#define heim_base_atomic_dec(x) atomic_dec_uint_nv((volatile uint_t *)(x))
+#define heim_base_atomic_type uint_t
+#define heim_base_atomic_max UINT_MAX
+
+#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((volatile void *)(t), (void *)(v))
+
+#elif defined(_AIX)
+
+#include <sys/atomic_op.h>
+
+#define heim_base_atomic_inc(x) (fetch_and_add((atomic_p)(x)) + 1)
+#define heim_base_atomic_dec(x) (fetch_and_add((atomic_p)(x)) - 1)
+#define heim_base_atomic_type unsigned int
+#define heim_base_atomic_max UINT_MAX
+
+static inline void *
+heim_base_exchange_pointer(void *p, void *newval)
+{
+ void *val = *(void **)p;
+
+ while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval))
+ ;
+
+ return val;
+}
+
+#elif defined(_WIN32)
+
+#define heim_base_atomic_inc(x) InterlockedIncrement(x)
+#define heim_base_atomic_dec(x) InterlockedDecrement(x)
+#define heim_base_atomic_type LONG
+#define heim_base_atomic_max MAXLONG
+
+#define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((t),(v))
+
+#else
+
+#define HEIM_BASE_NEED_ATOMIC_MUTEX 1
+extern HEIMDAL_MUTEX _heim_base_mutex;
+
+#define heim_base_atomic_type unsigned int
+
+static inline heim_base_atomic_type
+heim_base_atomic_inc(heim_base_atomic_type *x)
+{
+ heim_base_atomic_type t;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ t = ++(*x);
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return t;
+}
+
+static inline heim_base_atomic_type
+heim_base_atomic_dec(heim_base_atomic_type *x)
+{
+ heim_base_atomic_type t;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ t = --(*x);
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return t;
+}
+
+#define heim_base_atomic_max UINT_MAX
+
+#endif
+
+/* tagged strings/object/XXX */
+#define heim_base_is_tagged(x) (((uintptr_t)(x)) & 0x3)
+
+#define heim_base_is_tagged_object(x) ((((uintptr_t)(x)) & 0x3) == 1)
+#define heim_base_make_tagged_object(x, tid) \
+ ((heim_object_t)((((uintptr_t)(x)) << 5) | ((tid) << 2) | 0x1))
+#define heim_base_tagged_object_tid(x) ((((uintptr_t)(x)) & 0x1f) >> 2)
+#define heim_base_tagged_object_value(x) (((uintptr_t)(x)) >> 5)
+
+/*
+ *
+ */
+
+#undef HEIMDAL_NORETURN_ATTRIBUTE
+#define HEIMDAL_NORETURN_ATTRIBUTE
+#undef HEIMDAL_PRINTF_ATTRIBUTE
+#define HEIMDAL_PRINTF_ATTRIBUTE(x)
diff --git a/lib/base/bool.c b/lib/base/bool.c
new file mode 100644
index 000000000000..f7b66ee35838
--- /dev/null
+++ b/lib/base/bool.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+
+struct heim_type_data _heim_bool_object = {
+ HEIM_TID_BOOL,
+ "bool-object",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_bool_t
+heim_bool_create(int val)
+{
+ return heim_base_make_tagged_object(!!val, HEIM_TID_BOOL);
+}
+
+int
+heim_bool_val(heim_bool_t ptr)
+{
+ return heim_base_tagged_object_value(ptr);
+}
diff --git a/lib/base/bsearch.c b/lib/base/bsearch.c
new file mode 100644
index 000000000000..278962172683
--- /dev/null
+++ b/lib/base/bsearch.c
@@ -0,0 +1,886 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * 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 HOLDER 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 "baselocl.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <errno.h>
+#include <assert.h>
+
+/*
+ * This file contains functions for binary searching flat text in memory
+ * and in text files where each line is a [variable length] record.
+ * Each record has a key and an optional value separated from the key by
+ * unquoted whitespace. Whitespace in the key, and leading whitespace
+ * for the value, can be quoted with backslashes (but CR and LF must be
+ * quoted in such a way that they don't appear in the quoted result).
+ *
+ * Binary searching a tree are normally a dead simple algorithm. It
+ * turns out that binary searching flat text with *variable* length
+ * records is... tricky. There's no indexes to record beginning bytes,
+ * thus any index selected during the search is likely to fall in the
+ * middle of a record. When deciding to search a left sub-tree one
+ * might fail to find the last record in that sub-tree on account of the
+ * right boundary falling in the middle of it -- the chosen solution to
+ * this makes left sub-tree searches slightly less efficient than right
+ * sub-tree searches.
+ *
+ * If binary searching flat text in memory is tricky, using block-wise
+ * I/O instead is trickier! But it's necessary in order to support
+ * large files (which we either can't or wouldn't want to read or map
+ * into memory). Each block we read has to be large enough that the
+ * largest record can fit in it. And each block might start and/or end
+ * in the middle of a record. Here it is the right sub-tree searches
+ * that are less efficient than left sub-tree searches.
+ *
+ * bsearch_common() contains the common text block binary search code.
+ *
+ * _bsearch_text() is the interface for searching in-core text.
+ * _bsearch_file() is the interface for block-wise searching files.
+ */
+
+struct bsearch_file_handle {
+ int fd; /* file descriptor */
+ char *cache; /* cache bytes */
+ char *page; /* one double-size page worth of bytes */
+ size_t file_sz; /* file size */
+ size_t cache_sz; /* cache size */
+ size_t page_sz; /* page size */
+};
+
+/* Find a new-line */
+static const char *
+find_line(const char *buf, size_t i, size_t right)
+{
+ if (i == 0)
+ return &buf[i];
+ for (; i < right; i++) {
+ if (buf[i] == '\n') {
+ if ((i + 1) < right)
+ return &buf[i + 1];
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Common routine for binary searching text in core.
+ *
+ * Perform a binary search of a char array containing a block from a
+ * text file where each line is a record (LF and CRLF supported). Each
+ * record consists of a key followed by an optional value separated from
+ * the key by whitespace. Whitespace can be quoted with backslashes.
+ * It's the caller's responsibility to encode/decode keys/values if
+ * quoting is desired; newlines should be encoded such that a newline
+ * does not appear in the result.
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * Inputs:
+ *
+ * @buf String to search
+ * @sz Size of string to search
+ * @key Key string to search for
+ * @buf_is_start True if the buffer starts with a record, false if it
+ * starts in the middle of a record or if the caller
+ * doesn't know.
+ *
+ * Outputs:
+ *
+ * @value Location to store a copy of the value (caller must free)
+ * @location Record location if found else the location where the
+ * record should be inserted (index into @buf)
+ * @cmp Set to less than or greater than 0 to indicate that a
+ * key not found would have fit in an earlier or later
+ * part of a file. Callers should use this to decide
+ * whether to read a block to the left or to the right and
+ * search that.
+ * @loops Location to store a count of bisections required for
+ * search (useful for confirming logarithmic performance)
+ */
+static int
+bsearch_common(const char *buf, size_t sz, const char *key,
+ int buf_is_start, char **value, size_t *location,
+ int *cmp, size_t *loops)
+{
+ const char *linep;
+ size_t key_start, key_len; /* key string in buf */
+ size_t val_start, val_len; /* value string in buf */
+ int key_cmp = -1;
+ size_t k;
+ size_t l; /* left side of buffer for binary search */
+ size_t r; /* right side of buffer for binary search */
+ size_t rmax; /* right side of buffer for binary search */
+ size_t i; /* index into buffer, typically in the middle of l and r */
+ size_t loop_count = 0;
+ int ret = -1;
+
+ if (value)
+ *value = NULL;
+ if (cmp)
+ *cmp = 0;
+ if (loops)
+ *loops = 0;
+
+ /* Binary search; file should be sorted */
+ for (l = 0, r = rmax = sz, i = sz >> 1; i >= l && i < rmax; loop_count++) {
+ heim_assert(i < sz, "invalid aname2lname db index");
+
+ /* buf[i] is likely in the middle of a line; find the next line */
+ linep = find_line(buf, i, rmax);
+ k = linep ? linep - buf : i;
+ if (linep == NULL || k >= rmax) {
+ /*
+ * No new line found to the right; search to the left then
+ * but don't change rmax (this isn't optimal, but it's
+ * simple).
+ */
+ if (i == l)
+ break;
+ r = i;
+ i = l + ((r - l) >> 1);
+ continue;
+ }
+ i = k;
+ heim_assert(i >= l && i < rmax, "invalid aname2lname db index");
+
+ /* Got a line; check it */
+
+ /* Search for and split on unquoted whitespace */
+ val_start = 0;
+ for (key_start = i, key_len = 0, val_len = 0, k = i; k < rmax; k++) {
+ if (buf[k] == '\\') {
+ k++;
+ continue;
+ }
+ if (buf[k] == '\r' || buf[k] == '\n') {
+ /* We now know where the key ends, and there's no value */
+ key_len = k - i;
+ break;
+ }
+ if (!isspace((unsigned char)buf[k]))
+ continue;
+
+ while (k < rmax && isspace((unsigned char)buf[k])) {
+ key_len = k - i;
+ k++;
+ }
+ if (k < rmax)
+ val_start = k;
+ /* Find end of value */
+ for (; k < rmax && buf[k] != '\0'; k++) {
+ if (buf[k] == '\r' || buf[k] == '\n') {
+ val_len = k - val_start;
+ break;
+ }
+ }
+ break;
+ }
+
+ /*
+ * The following logic is for dealing with partial buffers,
+ * which we use for block-wise binary searches of large files
+ */
+ if (key_start == 0 && !buf_is_start) {
+ /*
+ * We're at the beginning of a block that might have started
+ * in the middle of a record whose "key" might well compare
+ * as greater than the key we're looking for, so we don't
+ * bother comparing -- we know key_cmp must be -1 here.
+ */
+ key_cmp = -1;
+ break;
+ }
+ if ((val_len && buf[val_start + val_len] != '\n') ||
+ (!val_len && buf[key_start + key_len] != '\n')) {
+ /*
+ * We're at the end of a block that ends in the middle of a
+ * record whose "key" might well compare as less than the
+ * key we're looking for, so we don't bother comparing -- we
+ * know key_cmp must be >= 0 but we can't tell. Our caller
+ * will end up reading a double-size block to handle this.
+ */
+ key_cmp = 1;
+ break;
+ }
+
+ key_cmp = strncmp(key, &buf[key_start], key_len);
+ if (key_cmp == 0 && strlen(key) != key_len)
+ key_cmp = 1;
+ if (key_cmp < 0) {
+ /* search left */
+ r = rmax = (linep - buf);
+ i = l + ((r - l) >> 1);
+ if (location)
+ *location = key_start;
+ } else if (key_cmp > 0) {
+ /* search right */
+ if (l == i)
+ break; /* not found */
+ l = i;
+ i = l + ((r - l) >> 1);
+ if (location)
+ *location = val_start + val_len;
+ } else {
+ /* match! */
+ if (location)
+ *location = key_start;
+ ret = 0;
+ if (val_len && value) {
+ /* Avoid strndup() so we don't need libroken here yet */
+ *value = malloc(val_len + 1);
+ if (!*value)
+ ret = errno;
+ (void) memcpy(*value, &buf[val_start], val_len);
+ (*value)[val_len] = '\0';
+ }
+ break;
+ }
+ }
+
+ if (cmp)
+ *cmp = key_cmp;
+ if (loops)
+ *loops = loop_count;
+
+ return ret;
+}
+
+/*
+ * Binary search a char array containing sorted text records separated
+ * by new-lines (or CRLF). Each record consists of a key and an
+ * optional value following the key, separated from the key by unquoted
+ * whitespace.
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * Inputs:
+ *
+ * @buf Char array pointer
+ * @buf_sz Size of buf
+ * @key Key to search for
+ *
+ * Outputs:
+ *
+ * @value Location where to put the value, if any (caller must free)
+ * @location Record location if found else the location where the record
+ * should be inserted (index into @buf)
+ * @loops Location where to put a number of loops (or comparisons)
+ * needed for the search (useful for benchmarking)
+ */
+int
+_bsearch_text(const char *buf, size_t buf_sz, const char *key,
+ char **value, size_t *location, size_t *loops)
+{
+ return bsearch_common(buf, buf_sz, key, 1, value, location, NULL, loops);
+}
+
+#define MAX_BLOCK_SIZE (1024 * 1024)
+#define DEFAULT_MAX_FILE_SIZE (1024 * 1024)
+/*
+ * Open a file for binary searching. The file will be read in entirely
+ * if it is smaller than @max_sz, else a cache of @max_sz bytes will be
+ * allocated.
+ *
+ * Returns 0 on success, else an error number or -1 if the file is empty.
+ *
+ * Inputs:
+ *
+ * @fname Name of file to open
+ * @max_sz Maximum size of cache to allocate, in bytes (if zero, default)
+ * @page_sz Page size (must be a power of two, larger than 256, smaller
+ * than 1MB; if zero use default)
+ *
+ * Outputs:
+ *
+ * @bfh Handle for use with _bsearch_file() and _bsearch_file_close()
+ * @reads Number of reads performed
+ */
+int
+_bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
+ bsearch_file_handle *bfh, size_t *reads)
+{
+ bsearch_file_handle new_bfh = NULL;
+ struct stat st;
+ size_t i;
+ int fd;
+ int ret;
+
+ *bfh = NULL;
+
+ if (reads)
+ *reads = 0;
+
+ fd = open(fname, O_RDONLY);
+ if (fd == -1)
+ return errno;
+
+ if (fstat(fd, &st) == -1) {
+ ret = errno;
+ goto err;
+ }
+
+ if (st.st_size == 0) {
+ ret = -1; /* no data -> no binary search */
+ goto err;
+ }
+
+ /* Validate / default arguments */
+ if (max_sz == 0)
+ max_sz = DEFAULT_MAX_FILE_SIZE;
+ for (i = page_sz; i; i >>= 1) {
+ /* Make sure page_sz is a power of two */
+ if ((i % 2) && (i >> 1)) {
+ page_sz = 0;
+ break;
+ }
+ }
+ if (page_sz == 0)
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+ page_sz = st.st_blksize;
+#else
+ page_sz = 4096;
+#endif
+ for (i = page_sz; i; i >>= 1) {
+ /* Make sure page_sz is a power of two */
+ if ((i % 2) && (i >> 1)) {
+ /* Can't happen! Filesystems always use powers of two! */
+ page_sz = 4096;
+ break;
+ }
+ }
+ if (page_sz > MAX_BLOCK_SIZE)
+ page_sz = MAX_BLOCK_SIZE;
+
+ new_bfh = calloc(1, sizeof (*new_bfh));
+ if (new_bfh == NULL) {
+ ret = ENOMEM;
+ goto err;
+ }
+
+ new_bfh->fd = fd;
+ new_bfh->page_sz = page_sz;
+ new_bfh->file_sz = st.st_size;
+
+ if (max_sz >= st.st_size) {
+ /* Whole-file method */
+ new_bfh->cache = malloc(st.st_size + 1);
+ if (new_bfh->cache) {
+ new_bfh->cache[st.st_size] = '\0';
+ new_bfh->cache_sz = st.st_size;
+ ret = read(fd, new_bfh->cache, st.st_size);
+ if (ret < 0) {
+ ret = errno;
+ goto err;
+ }
+ if (ret != st.st_size) {
+ ret = EIO; /* XXX ??? */
+ goto err;
+ }
+ if (reads)
+ *reads = 1;
+ (void) close(fd);
+ new_bfh->fd = -1;
+ *bfh = new_bfh;
+ return 0;
+ }
+ }
+
+ /* Block-size method, or above malloc() failed */
+ new_bfh->page = malloc(new_bfh->page_sz << 1);
+ if (new_bfh->page == NULL) {
+ /* Can't even allocate a single double-size page! */
+ ret = ENOMEM;
+ goto err;
+ }
+
+ new_bfh->cache_sz = max_sz < st.st_size ? max_sz : st.st_size;
+ new_bfh->cache = malloc(new_bfh->cache_sz);
+ *bfh = new_bfh;
+
+ /*
+ * malloc() may have failed because we were asking for a lot of
+ * memory, but we may still be able to operate without a cache,
+ * so let's not fail.
+ */
+ if (new_bfh->cache == NULL) {
+ new_bfh->cache_sz = 0;
+ return 0;
+ }
+
+ /* Initialize cache */
+ for (i = 0; i < new_bfh->cache_sz; i += new_bfh->page_sz)
+ new_bfh->cache[i] = '\0';
+ return 0;
+
+err:
+ (void) close(fd);
+ if (new_bfh) {
+ free(new_bfh->page);
+ free(new_bfh->cache);
+ free(new_bfh);
+ }
+ return ret;
+}
+
+/*
+ * Indicate whether the given binary search file handle will be searched
+ * with block-wise method.
+ */
+void
+_bsearch_file_info(bsearch_file_handle bfh,
+ size_t *page_sz, size_t *max_sz, int *blockwise)
+{
+ if (page_sz)
+ *page_sz = bfh->page_sz;
+ if (max_sz)
+ *max_sz = bfh->cache_sz;
+ if (blockwise)
+ *blockwise = (bfh->file_sz != bfh->cache_sz);
+}
+
+/*
+ * Close the given binary file search handle.
+ *
+ * Inputs:
+ *
+ * @bfh Pointer to variable containing handle to close.
+ */
+void
+_bsearch_file_close(bsearch_file_handle *bfh)
+{
+ if (!*bfh)
+ return;
+ if ((*bfh)->fd >= 0)
+ (void) close((*bfh)->fd);
+ if ((*bfh)->page)
+ free((*bfh)->page);
+ if ((*bfh)->cache)
+ free((*bfh)->cache);
+ free(*bfh);
+ *bfh = NULL;
+}
+
+/*
+ * Private function to get a page from a cache. The cache is a char
+ * array of 2^n - 1 double-size page worth of bytes, where n is the
+ * number of tree levels that the cache stores. The cache can be
+ * smaller than n implies.
+ *
+ * The page may or may not be valid. If the first byte of it is NUL
+ * then it's not valid, else it is.
+ *
+ * Returns 1 if page is in cache and valid, 0 if the cache is too small
+ * or the page is invalid. The page address is output in @buf if the
+ * cache is large enough to contain it regardless of whether the page is
+ * valid.
+ *
+ * Inputs:
+ *
+ * @bfh Binary search file handle
+ * @level Level in the tree that we want a page for
+ * @page_idx Page number in the given level (0..2^level - 1)
+ *
+ * Outputs:
+ *
+ * @buf Set to address of page if the cache is large enough
+ */
+static int
+get_page_from_cache(bsearch_file_handle bfh, size_t level, size_t page_idx,
+ char **buf)
+{
+ size_t idx = 0;
+ size_t page_sz;
+
+ page_sz = bfh->page_sz << 1; /* we use double-size pages in the cache */
+
+ *buf = NULL;
+
+ /*
+ * Compute index into cache. The cache is basically an array of
+ * double-size pages. The first (zeroth) double-size page in the
+ * cache will be the middle page of the file -- the root of the
+ * tree. The next two double-size pages will be the left and right
+ * pages of the second level in the tree. The next four double-size
+ * pages will be the four pages at the next level. And so on for as
+ * many pages as fit in the cache.
+ *
+ * The page index is the number of the page at the given level. We
+ * then compute (2^level - 1 + page index) * 2page size, check that
+ * we have that in the cache, check that the page has been read (it
+ * doesn't start with NUL).
+ */
+ if (level)
+ idx = (1 << level) - 1 + page_idx;
+ if (((idx + 1) * page_sz * 2) > bfh->cache_sz)
+ return 0;
+
+ *buf = &bfh->cache[idx * page_sz * 2];
+ if (bfh->cache[idx * page_sz * 2] == '\0')
+ return 0; /* cache[idx] == NUL -> page not loaded in cache */
+ return 1;
+}
+
+/*
+ * Private function to read a page of @page_sz from @fd at offset @off
+ * into @buf, outputing the number of bytes read, which will be the same
+ * as @page_sz unless the page being read is the last page, in which
+ * case the number of remaining bytes in the file will be output.
+ *
+ * Returns 0 on success or an errno value otherwise (EIO if reads are
+ * short).
+ *
+ * Inputs:
+ *
+ * @bfh Binary search file handle
+ * @level Level in the binary search tree that we're at
+ * @page_idx Page "index" at the @level of the tree that we want
+ * @page Actual page number that we want
+ * want_double Whether we need a page or double page read
+ *
+ * Outputs:
+ *
+ * @buf Page read or cached
+ * @bytes Bytes read (may be less than page or double page size in
+ * the case of the last page, of course)
+ */
+static int
+read_page(bsearch_file_handle bfh, size_t level, size_t page_idx, size_t page,
+ int want_double, const char **buf, size_t *bytes)
+{
+ int ret;
+ off_t off;
+ size_t expected;
+ size_t wanted;
+ char *page_buf;
+
+ /* Figure out where we're reading and how much */
+ off = page * bfh->page_sz;
+ if (off < 0)
+ return EOVERFLOW;
+
+ wanted = bfh->page_sz << want_double;
+ expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
+
+ if (get_page_from_cache(bfh, level, page_idx, &page_buf)) {
+ *buf = page_buf;
+ *bytes = expected;
+ return 0; /* found in cache */
+ }
+
+
+ *bytes = 0;
+ *buf = NULL;
+
+ /* OK, we have to read a page or double-size page */
+
+ if (page_buf)
+ want_double = 1; /* we'll be caching; we cache double-size pages */
+ else
+ page_buf = bfh->page; /* we won't cache this page */
+
+ wanted = bfh->page_sz << want_double;
+ expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
+
+#ifdef HAVE_PREAD
+ ret = pread(bfh->fd, page_buf, expected, off);
+#else
+ if (lseek(bfh->fd, off, SEEK_SET) == (off_t)-1)
+ return errno;
+ ret = read(bfh->fd, page_buf, expected);
+#endif
+ if (ret < 0)
+ return errno;
+
+ if (ret != expected)
+ return EIO; /* XXX ??? */
+
+ *buf = page_buf;
+ *bytes = expected;
+ return 0;
+}
+
+/*
+ * Perform a binary search of a file where each line is a record (LF and
+ * CRLF supported). Each record consists of a key followed by an
+ * optional value separated from the key by whitespace. Whitespace can
+ * be quoted with backslashes. It's the caller's responsibility to
+ * encode/decode keys/values if quoting is desired; newlines should be
+ * encoded such that a newline does not appear in the result.
+ *
+ * The search is done with block-wise I/O (i.e., the whole file is not
+ * read into memory).
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * NOTE: We could improve this by not freeing the buffer, instead
+ * requiring that the caller provide it. Further, we could cache
+ * the top N levels of [double-size] pages (2^N - 1 pages), which
+ * should speed up most searches by reducing the number of reads
+ * by N.
+ *
+ * Inputs:
+ *
+ * @fd File descriptor (file to search)
+ * @page_sz Page size (if zero then the file's st_blksize will be used)
+ * @key Key string to search for
+ *
+ * Outputs:
+ *
+ * @value Location to store a copy of the value (caller must free)
+ * @location Record location if found else the location where the
+ * record should be inserted (index into @buf)
+ * @loops Location to store a count of bisections required for
+ * search (useful for confirming logarithmic performance)
+ * @reads Location to store a count of pages read during search
+ * (useful for confirming logarithmic performance)
+ */
+int
+_bsearch_file(bsearch_file_handle bfh, const char *key,
+ char **value, size_t *location, size_t *loops, size_t *reads)
+{
+ int ret;
+ const char *buf;
+ size_t buf_sz;
+ size_t page, l, r;
+ size_t my_reads = 0;
+ size_t my_loops_total = 0;
+ size_t my_loops;
+ size_t level; /* level in the tree */
+ size_t page_idx = 0; /* page number in the tree level */
+ size_t buf_location;
+ int cmp;
+ int buf_ends_in_eol = 0;
+ int buf_is_start = 0;
+
+ if (reads)
+ *reads = 0;
+
+ /* If whole file is in memory then search that and we're done */
+ if (bfh->file_sz == bfh->cache_sz)
+ return _bsearch_text(bfh->cache, bfh->cache_sz, key, value, location, loops);
+
+ /* Else block-wise binary search */
+
+ if (value)
+ *value = NULL;
+ if (loops)
+ *loops = 0;
+
+ l = 0;
+ r = (bfh->file_sz / bfh->page_sz) + 1;
+ for (level = 0, page = r >> 1; page >= l && page < r ; level++) {
+ ret = read_page(bfh, level, page_idx, page, 0, &buf, &buf_sz);
+ if (ret != 0)
+ return ret;
+ my_reads++;
+ if (buf[buf_sz - 1] == '\r' || buf[buf_sz - 1] == '\n')
+ buf_ends_in_eol = 1;
+ else
+ buf_ends_in_eol = 0;
+
+ buf_is_start = page == 0 ? 1 : 0;
+ ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
+ value, &buf_location, &cmp, &my_loops);
+ if (ret > 0)
+ return ret;
+ /* Found or no we update stats */
+ my_loops_total += my_loops;
+ if (loops)
+ *loops = my_loops_total;
+ if (reads)
+ *reads = my_reads;
+ if (location)
+ *location = page * bfh->page_sz + buf_location;
+ if (ret == 0)
+ return 0; /* found! */
+ /* Not found */
+ if (cmp < 0) {
+ /* Search left */
+ page_idx <<= 1;
+ r = page;
+ page = l + ((r - l) >> 1);
+ continue;
+ } else {
+ /*
+ * Search right, but first search the current and next
+ * blocks in case that the record we're looking for either
+ * straddles the boundary between this and the next record,
+ * or in case the record starts exactly at the next page.
+ */
+ heim_assert(cmp > 0, "cmp > 0");
+
+ if (!buf_ends_in_eol || page == l || page == (r - 1)) {
+ ret = read_page(bfh, level, page_idx, page, 1, &buf, &buf_sz);
+ if (ret != 0)
+ return ret;
+ my_reads++;
+
+ buf_is_start = page == l ? 1 : 0;
+
+ ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
+ value, &buf_location, &cmp, &my_loops);
+ if (ret > 0)
+ return ret;
+ my_loops_total += my_loops;
+ if (loops)
+ *loops = my_loops_total;
+ if (reads)
+ *reads = my_reads;
+ if (location)
+ *location = page * bfh->page_sz + buf_location;
+ if (ret == 0)
+ return 0;
+ }
+
+ /* Oh well, search right */
+ if (l == page && r == (l + 1))
+ break;
+ page_idx = (page_idx << 1) + 1;
+ l = page;
+ page = l + ((r - l) >> 1);
+ continue;
+ }
+ }
+ return -1;
+}
+
+
+static int
+stdb_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ bsearch_file_handle bfh;
+ char *p;
+ int ret;
+
+ if (error)
+ *error = NULL;
+ if (dbname == NULL || *dbname == '\0') {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ N_("DB name required for sorted-text DB "
+ "plugin", ""));
+ return EINVAL;
+ }
+ p = strrchr(dbname, '.');
+ if (p == NULL || strcmp(p, ".txt") != 0) {
+ if (error)
+ *error = heim_error_create(ENOTSUP,
+ N_("Text file (name ending in .txt) "
+ "required for sorted-text DB plugin",
+ ""));
+ return ENOTSUP;
+ }
+
+ ret = _bsearch_file_open(dbname, 0, 0, &bfh, NULL);
+ if (ret)
+ return ret;
+
+ *db = bfh;
+ return 0;
+}
+
+static int
+stdb_close(void *db, heim_error_t *error)
+{
+ bsearch_file_handle bfh = db;
+
+ if (error)
+ *error = NULL;
+ _bsearch_file_close(&bfh);
+ return 0;
+}
+
+static heim_data_t
+stdb_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ bsearch_file_handle bfh = db;
+ const char *k;
+ char *v;
+ heim_data_t value;
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (table != HSTR(""))
+ return NULL;
+
+ if (heim_get_tid(key) == HEIM_TID_STRING)
+ k = heim_string_get_utf8((heim_string_t)key);
+ else
+ k = (const char *)heim_data_get_ptr(key);
+ ret = _bsearch_file(bfh, k, &v, NULL, NULL, NULL);
+ if (ret != 0) {
+ if (ret > 0 && error)
+ *error = heim_error_create(ret, "%s", strerror(ret));
+ return NULL;
+ }
+ value = heim_data_create(v, strlen(v));
+ free(v);
+ /* XXX Handle ENOMEM */
+ return value;
+}
+
+struct heim_db_type heim_sorted_text_file_dbtype = {
+ 1, stdb_open, NULL, stdb_close, NULL, NULL, NULL, NULL, NULL, NULL,
+ stdb_copy_value, NULL, NULL, NULL
+};
diff --git a/lib/base/data.c b/lib/base/data.c
new file mode 100644
index 000000000000..4aa6efc66774
--- /dev/null
+++ b/lib/base/data.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+#include <string.h>
+
+static void
+data_dealloc(void *ptr)
+{
+ heim_data_t d = ptr;
+ heim_octet_string *os = (heim_octet_string *)d;
+ heim_data_free_f_t *deallocp;
+ heim_data_free_f_t dealloc;
+
+ if (os->data == NULL)
+ return;
+
+ /* Possible string ref */
+ deallocp = _heim_get_isaextra(os, 0);
+ dealloc = *deallocp;
+ if (dealloc != NULL)
+ dealloc(os->data);
+}
+
+static int
+data_cmp(void *a, void *b)
+{
+ heim_octet_string *osa = a, *osb = b;
+ if (osa->length != osb->length)
+ return osa->length - osb->length;
+ return memcmp(osa->data, osb->data, osa->length);
+}
+
+static unsigned long
+data_hash(void *ptr)
+{
+ heim_octet_string *os = ptr;
+ const unsigned char *s = os->data;
+
+ if (os->length < 4)
+ return os->length;
+ return s[0] | (s[1] << 8) |
+ (s[os->length - 2] << 16) | (s[os->length - 1] << 24);
+}
+
+struct heim_type_data _heim_data_object = {
+ HEIM_TID_DATA,
+ "data-object",
+ NULL,
+ data_dealloc,
+ NULL,
+ data_cmp,
+ data_hash,
+ NULL
+};
+
+/**
+ * Create a data object
+ *
+ * @param string the string to create, must be an utf8 string
+ *
+ * @return string object
+ */
+
+heim_data_t
+heim_data_create(const void *data, size_t length)
+{
+ heim_octet_string *os;
+
+ os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
+ if (os) {
+ os->data = (uint8_t *)os + sizeof(*os);
+ os->length = length;
+ memcpy(os->data, data, length);
+ }
+ return (heim_data_t)os;
+}
+
+heim_data_t
+heim_data_ref_create(const void *data, size_t length,
+ heim_data_free_f_t dealloc)
+{
+ heim_octet_string *os;
+ heim_data_free_f_t *deallocp;
+
+ os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
+ if (os) {
+ os->data = (void *)data;
+ os->length = length;
+ deallocp = _heim_get_isaextra(os, 0);
+ *deallocp = dealloc;
+ }
+ return (heim_data_t)os;
+}
+
+
+/**
+ * Return the type ID of data objects
+ *
+ * @return type id of data objects
+ */
+
+heim_tid_t
+heim_data_get_type_id(void)
+{
+ return HEIM_TID_DATA;
+}
+
+/**
+ * Get the data value of the content.
+ *
+ * @param data the data object to get the value from
+ *
+ * @return a heim_octet_string
+ */
+
+const heim_octet_string *
+heim_data_get_data(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return (const heim_octet_string *)data;
+}
+
+const void *
+heim_data_get_ptr(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return ((const heim_octet_string *)data)->data;
+}
+
+size_t heim_data_get_length(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return ((const heim_octet_string *)data)->length;
+}
diff --git a/lib/base/db.c b/lib/base/db.c
new file mode 100644
index 000000000000..944091684882
--- /dev/null
+++ b/lib/base/db.c
@@ -0,0 +1,1727 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * 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 HOLDER 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.
+ */
+
+/*
+ * This is a pluggable simple DB abstraction, with a simple get/set/
+ * delete key/value pair interface.
+ *
+ * Plugins may provide any of the following optional features:
+ *
+ * - tables -- multiple attribute/value tables in one DB
+ * - locking
+ * - transactions (i.e., allow any heim_object_t as key or value)
+ * - transcoding of values
+ *
+ * Stackable plugins that provide missing optional features are
+ * possible.
+ *
+ * Any plugin that provides locking will also provide transactions, but
+ * those transactions will not be atomic in the face of failures (a
+ * memory-based rollback log is used).
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <io.h>
+#else
+#include <sys/file.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "baselocl.h"
+#include <base64.h>
+
+#define HEIM_ENOMEM(ep) \
+ (((ep) && !*(ep)) ? \
+ heim_error_get_code((*(ep) = heim_error_create_enomem())) : ENOMEM)
+
+#define HEIM_ERROR_HELPER(ep, ec, args) \
+ (((ep) && !*(ep)) ? \
+ heim_error_get_code((*(ep) = heim_error_create args)) : (ec))
+
+#define HEIM_ERROR(ep, ec, args) \
+ (ec == ENOMEM) ? HEIM_ENOMEM(ep) : HEIM_ERROR_HELPER(ep, ec, args);
+
+static heim_string_t to_base64(heim_data_t, heim_error_t *);
+static heim_data_t from_base64(heim_string_t, heim_error_t *);
+
+static int open_file(const char *, int , int, int *, heim_error_t *);
+static int read_json(const char *, heim_object_t *, heim_error_t *);
+static struct heim_db_type json_dbt;
+
+static void db_dealloc(void *ptr);
+
+struct heim_type_data db_object = {
+ HEIM_TID_DB,
+ "db-object",
+ NULL,
+ db_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+static heim_base_once_t db_plugin_init_once = HEIM_BASE_ONCE_INIT;
+
+static heim_dict_t db_plugins;
+
+typedef struct db_plugin {
+ heim_string_t name;
+ heim_db_plug_open_f_t openf;
+ heim_db_plug_clone_f_t clonef;
+ heim_db_plug_close_f_t closef;
+ heim_db_plug_lock_f_t lockf;
+ heim_db_plug_unlock_f_t unlockf;
+ heim_db_plug_sync_f_t syncf;
+ heim_db_plug_begin_f_t beginf;
+ heim_db_plug_commit_f_t commitf;
+ heim_db_plug_rollback_f_t rollbackf;
+ heim_db_plug_copy_value_f_t copyf;
+ heim_db_plug_set_value_f_t setf;
+ heim_db_plug_del_key_f_t delf;
+ heim_db_plug_iter_f_t iterf;
+ void *data;
+} db_plugin_desc, *db_plugin;
+
+struct heim_db_data {
+ db_plugin plug;
+ heim_string_t dbtype;
+ heim_string_t dbname;
+ heim_dict_t options;
+ void *db_data;
+ heim_data_t to_release;
+ heim_error_t error;
+ int ret;
+ unsigned int in_transaction:1;
+ unsigned int ro:1;
+ unsigned int ro_tx:1;
+ heim_dict_t set_keys;
+ heim_dict_t del_keys;
+ heim_string_t current_table;
+};
+
+static int
+db_do_log_actions(heim_db_t db, heim_error_t *error);
+static int
+db_replay_log(heim_db_t db, heim_error_t *error);
+
+static HEIMDAL_MUTEX db_type_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+static void
+db_init_plugins_once(void *arg)
+{
+ db_plugins = heim_retain(arg);
+}
+
+static void
+plugin_dealloc(void *arg)
+{
+ db_plugin plug = arg;
+
+ heim_release(plug->name);
+}
+
+/** heim_db_register
+ * @brief Registers a DB type for use with heim_db_create().
+ *
+ * @param dbtype Name of DB type
+ * @param data Private data argument to the dbtype's openf method
+ * @param plugin Structure with DB type methods (function pointers)
+ *
+ * Backends that provide begin/commit/rollback methods must provide ACID
+ * semantics.
+ *
+ * The registered DB type will have ACID semantics for backends that do
+ * not provide begin/commit/rollback methods but do provide lock/unlock
+ * and rdjournal/wrjournal methods (using a replay log journalling
+ * scheme).
+ *
+ * If the registered DB type does not natively provide read vs. write
+ * transaction isolation but does provide a lock method then the DB will
+ * provide read/write transaction isolation.
+ *
+ * @return ENOMEM on failure, else 0.
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_register(const char *dbtype,
+ void *data,
+ struct heim_db_type *plugin)
+{
+ heim_dict_t plugins;
+ heim_string_t s;
+ db_plugin plug, plug2;
+ int ret = 0;
+
+ if ((plugin->beginf != NULL && plugin->commitf == NULL) ||
+ (plugin->beginf != NULL && plugin->rollbackf == NULL) ||
+ (plugin->lockf != NULL && plugin->unlockf == NULL) ||
+ plugin->copyf == NULL)
+ heim_abort("Invalid DB plugin; make sure methods are paired");
+
+ /* Initialize */
+ plugins = heim_dict_create(11);
+ if (plugins == NULL)
+ return ENOMEM;
+ heim_base_once_f(&db_plugin_init_once, plugins, db_init_plugins_once);
+ heim_release(plugins);
+ heim_assert(db_plugins != NULL, "heim_db plugin table initialized");
+
+ s = heim_string_create(dbtype);
+ if (s == NULL)
+ return ENOMEM;
+
+ plug = heim_alloc(sizeof (*plug), "db_plug", plugin_dealloc);
+ if (plug == NULL) {
+ heim_release(s);
+ return ENOMEM;
+ }
+
+ plug->name = heim_retain(s);
+ plug->openf = plugin->openf;
+ plug->clonef = plugin->clonef;
+ plug->closef = plugin->closef;
+ plug->lockf = plugin->lockf;
+ plug->unlockf = plugin->unlockf;
+ plug->syncf = plugin->syncf;
+ plug->beginf = plugin->beginf;
+ plug->commitf = plugin->commitf;
+ plug->rollbackf = plugin->rollbackf;
+ plug->copyf = plugin->copyf;
+ plug->setf = plugin->setf;
+ plug->delf = plugin->delf;
+ plug->iterf = plugin->iterf;
+ plug->data = data;
+
+ HEIMDAL_MUTEX_lock(&db_type_mutex);
+ plug2 = heim_dict_get_value(db_plugins, s);
+ if (plug2 == NULL)
+ ret = heim_dict_set_value(db_plugins, s, plug);
+ HEIMDAL_MUTEX_unlock(&db_type_mutex);
+ heim_release(plug);
+ heim_release(s);
+
+ return ret;
+}
+
+static void
+db_dealloc(void *arg)
+{
+ heim_db_t db = arg;
+ heim_assert(!db->in_transaction,
+ "rollback or commit heim_db_t before releasing it");
+ if (db->db_data)
+ (void) db->plug->closef(db->db_data, NULL);
+ heim_release(db->to_release);
+ heim_release(db->dbtype);
+ heim_release(db->dbname);
+ heim_release(db->options);
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ heim_release(db->error);
+}
+
+struct dbtype_iter {
+ heim_db_t db;
+ const char *dbname;
+ heim_dict_t options;
+ heim_error_t *error;
+};
+
+/*
+ * Helper to create a DB handle with the first registered DB type that
+ * can open the given DB. This is useful when the app doesn't know the
+ * DB type a priori. This assumes that DB types can "taste" DBs, either
+ * from the filename extension or from the actual file contents.
+ */
+static void
+dbtype_iter2create_f(heim_object_t dbtype, heim_object_t junk, void *arg)
+{
+ struct dbtype_iter *iter_ctx = arg;
+
+ if (iter_ctx->db != NULL)
+ return;
+ iter_ctx->db = heim_db_create(heim_string_get_utf8(dbtype),
+ iter_ctx->dbname, iter_ctx->options,
+ iter_ctx->error);
+}
+
+/**
+ * Open a database of the given dbtype.
+ *
+ * Database type names can be composed of one or more pseudo-DB types
+ * and one concrete DB type joined with a '+' between each. For
+ * example: "transaction+bdb" might be a Berkeley DB with a layer above
+ * that provides transactions.
+ *
+ * Options may be provided via a dict (an associative array). Existing
+ * options include:
+ *
+ * - "create", with any value (create if DB doesn't exist)
+ * - "exclusive", with any value (exclusive create)
+ * - "truncate", with any value (truncate the DB)
+ * - "read-only", with any value (disallow writes)
+ * - "sync", with any value (make transactions durable)
+ * - "journal-name", with a string value naming a journal file name
+ *
+ * @param dbtype Name of DB type
+ * @param dbname Name of DB (likely a file path)
+ * @param options Options dict
+ * @param db Output open DB handle
+ * @param error Output error object
+ *
+ * @return a DB handle
+ *
+ * @addtogroup heimbase
+ */
+heim_db_t
+heim_db_create(const char *dbtype, const char *dbname,
+ heim_dict_t options, heim_error_t *error)
+{
+ heim_string_t s;
+ char *p;
+ db_plugin plug;
+ heim_db_t db;
+ int ret = 0;
+
+ if (options == NULL) {
+ options = heim_dict_create(11);
+ if (options == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ } else {
+ (void) heim_retain(options);
+ }
+
+ if (db_plugins == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ if (dbtype == NULL || *dbtype == '\0') {
+ struct dbtype_iter iter_ctx = { NULL, dbname, options, error};
+
+ /* Try all dbtypes */
+ heim_dict_iterate_f(db_plugins, &iter_ctx, dbtype_iter2create_f);
+ heim_release(options);
+ return iter_ctx.db;
+ } else if (strstr(dbtype, "json")) {
+ (void) heim_db_register(dbtype, NULL, &json_dbt);
+ }
+
+ /*
+ * Allow for dbtypes that are composed from pseudo-dbtypes chained
+ * to a real DB type with '+'. For example a pseudo-dbtype might
+ * add locking, transactions, transcoding of values, ...
+ */
+ p = strchr(dbtype, '+');
+ if (p != NULL)
+ s = heim_string_create_with_bytes(dbtype, p - dbtype);
+ else
+ s = heim_string_create(dbtype);
+ if (s == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ HEIMDAL_MUTEX_lock(&db_type_mutex);
+ plug = heim_dict_get_value(db_plugins, s);
+ HEIMDAL_MUTEX_unlock(&db_type_mutex);
+ heim_release(s);
+ if (plug == NULL) {
+ if (error)
+ *error = heim_error_create(ENOENT,
+ N_("Heimdal DB plugin not found: %s", ""),
+ dbtype);
+ heim_release(options);
+ return NULL;
+ }
+
+ db = _heim_alloc_object(&db_object, sizeof(*db));
+ if (db == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->plug = plug;
+ db->options = options;
+
+ ret = plug->openf(plug->data, dbtype, dbname, options, &db->db_data, error);
+ if (ret) {
+ heim_release(db);
+ if (error && *error == NULL)
+ *error = heim_error_create(ENOENT,
+ N_("Heimdal DB could not be opened: %s", ""),
+ dbname);
+ return NULL;
+ }
+
+ ret = db_replay_log(db, error);
+ if (ret) {
+ heim_release(db);
+ return NULL;
+ }
+
+ if (plug->clonef == NULL) {
+ db->dbtype = heim_string_create(dbtype);
+ db->dbname = heim_string_create(dbname);
+
+ if (!db->dbtype || ! db->dbname) {
+ heim_release(db);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ }
+
+ return db;
+}
+
+/**
+ * Clone (duplicate) an open DB handle.
+ *
+ * This is useful for multi-threaded applications. Applications must
+ * synchronize access to any given DB handle.
+ *
+ * Returns EBUSY if there is an open transaction for the input db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return a DB handle
+ *
+ * @addtogroup heimbase
+ */
+heim_db_t
+heim_db_clone(heim_db_t db, heim_error_t *error)
+{
+ heim_db_t result;
+ int ret;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ heim_abort("Expected a database");
+ if (db->in_transaction)
+ heim_abort("DB handle is busy");
+
+ if (db->plug->clonef == NULL) {
+ return heim_db_create(heim_string_get_utf8(db->dbtype),
+ heim_string_get_utf8(db->dbname),
+ db->options, error);
+ }
+
+ result = _heim_alloc_object(&db_object, sizeof(*result));
+ if (result == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ result->set_keys = NULL;
+ result->del_keys = NULL;
+ ret = db->plug->clonef(db->db_data, &result->db_data, error);
+ if (ret) {
+ heim_release(result);
+ if (error && !*error)
+ *error = heim_error_create(ENOENT,
+ N_("Could not re-open DB while cloning", ""));
+ return NULL;
+ }
+ db->db_data = NULL;
+ return result;
+}
+
+/**
+ * Open a transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_begin(heim_db_t db, int read_only, heim_error_t *error)
+{
+ int ret;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (db->in_transaction && (read_only || !db->ro_tx || (!read_only && !db->ro_tx)))
+ heim_abort("DB already in transaction");
+
+ if (db->plug->setf == NULL || db->plug->delf == NULL)
+ return EINVAL;
+
+ if (db->plug->beginf) {
+ ret = db->plug->beginf(db->db_data, read_only, error);
+ if (ret)
+ return ret;
+ } else if (!db->in_transaction) {
+ /* Try to emulate transactions */
+
+ if (db->plug->lockf == NULL)
+ return EINVAL; /* can't lock? -> no transactions */
+
+ /* Assume unlock provides sync/durability */
+ ret = db->plug->lockf(db->db_data, read_only, error);
+ if (ret)
+ return ret;
+
+ ret = db_replay_log(db, error);
+ if (ret) {
+ ret = db->plug->unlockf(db->db_data, error);
+ return ret;
+ }
+
+ db->set_keys = heim_dict_create(11);
+ if (db->set_keys == NULL)
+ return ENOMEM;
+ db->del_keys = heim_dict_create(11);
+ if (db->del_keys == NULL) {
+ heim_release(db->set_keys);
+ db->set_keys = NULL;
+ return ENOMEM;
+ }
+ } else {
+ heim_assert(read_only == 0, "Internal error");
+ ret = db->plug->lockf(db->db_data, 0, error);
+ if (ret)
+ return ret;
+ }
+ db->in_transaction = 1;
+ db->ro_tx = !!read_only;
+ return 0;
+}
+
+/**
+ * Commit an open transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_commit(heim_db_t db, heim_error_t *error)
+{
+ int ret, ret2;
+ heim_string_t journal_fname = NULL;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+ if (!db->in_transaction)
+ return 0;
+ if (db->plug->commitf == NULL && db->plug->lockf == NULL)
+ return EINVAL;
+
+ if (db->plug->commitf != NULL) {
+ ret = db->plug->commitf(db->db_data, error);
+ if (ret)
+ (void) db->plug->rollbackf(db->db_data, error);
+
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+ return ret;
+ }
+
+ if (db->ro_tx) {
+ ret = 0;
+ goto done;
+ }
+
+ if (db->options == NULL)
+ journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename"));
+
+ if (journal_fname != NULL) {
+ heim_array_t a;
+ heim_string_t journal_contents;
+ size_t len, bytes;
+ int save_errno;
+
+ /* Create contents for replay log */
+ ret = ENOMEM;
+ a = heim_array_create();
+ if (a == NULL)
+ goto err;
+ ret = heim_array_append_value(a, db->set_keys);
+ if (ret) {
+ heim_release(a);
+ goto err;
+ }
+ ret = heim_array_append_value(a, db->del_keys);
+ if (ret) {
+ heim_release(a);
+ goto err;
+ }
+ journal_contents = heim_json_copy_serialize(a, 0, error);
+ heim_release(a);
+
+ /* Write replay log */
+ if (journal_fname != NULL) {
+ int fd;
+
+ ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error);
+ if (ret) {
+ heim_release(journal_contents);
+ goto err;
+ }
+ len = strlen(heim_string_get_utf8(journal_contents));
+ bytes = write(fd, heim_string_get_utf8(journal_contents), len);
+ save_errno = errno;
+ heim_release(journal_contents);
+ ret = close(fd);
+ if (bytes != len) {
+ /* Truncate replay log */
+ (void) open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error);
+ ret = save_errno;
+ goto err;
+ }
+ if (ret)
+ goto err;
+ }
+ }
+
+ /* Apply logged actions */
+ ret = db_do_log_actions(db, error);
+ if (ret)
+ return ret;
+
+ if (db->plug->syncf != NULL) {
+ /* fsync() or whatever */
+ ret = db->plug->syncf(db->db_data, error);
+ if (ret)
+ return ret;
+ }
+
+ /* Truncate replay log and we're done */
+ if (journal_fname != NULL) {
+ int fd;
+
+ ret2 = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error);
+ if (ret2 == 0)
+ (void) close(fd);
+ }
+
+ /*
+ * Clean up; if we failed to remore the replay log that's OK, we'll
+ * handle that again in heim_db_commit()
+ */
+done:
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+
+ ret2 = db->plug->unlockf(db->db_data, error);
+ if (ret == 0)
+ ret = ret2;
+
+ return ret;
+
+err:
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Error while committing transaction: %s", ""),
+ strerror(ret)));
+}
+
+/**
+ * Rollback an open transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_rollback(heim_db_t db, heim_error_t *error)
+{
+ int ret = 0;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+ if (!db->in_transaction)
+ return 0;
+
+ if (db->plug->rollbackf != NULL)
+ ret = db->plug->rollbackf(db->db_data, error);
+ else if (db->plug->unlockf != NULL)
+ ret = db->plug->unlockf(db->db_data, error);
+
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+
+ return ret;
+}
+
+/**
+ * Get type ID of heim_db_t objects.
+ *
+ * @addtogroup heimbase
+ */
+heim_tid_t
+heim_db_get_type_id(void)
+{
+ return HEIM_TID_DB;
+}
+
+heim_data_t
+_heim_db_get_value(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_release(db->to_release);
+ db->to_release = heim_db_copy_value(db, table, key, error);
+ return db->to_release;
+}
+
+/**
+ * Lookup a key's value in the DB.
+ *
+ * Returns 0 on success, -1 if the key does not exist in the DB, or a
+ * system error number on failure.
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param error Output error object
+ *
+ * @return the value (retained), if there is one for the given key
+ *
+ * @addtogroup heimbase
+ */
+heim_data_t
+heim_db_copy_value(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_object_t v;
+ heim_data_t result;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return NULL;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (db->in_transaction) {
+ heim_string_t key64;
+
+ key64 = to_base64(key, error);
+ if (key64 == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ v = heim_path_copy(db->set_keys, error, table, key64, NULL);
+ if (v != NULL) {
+ heim_release(key64);
+ return v;
+ }
+ v = heim_path_copy(db->del_keys, error, table, key64, NULL); /* can't be NULL */
+ heim_release(key64);
+ if (v != NULL)
+ return NULL;
+ }
+
+ result = db->plug->copyf(db->db_data, table, key, error);
+
+ return result;
+}
+
+/**
+ * Set a key's value in the DB.
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param value Value (if NULL the key will be deleted, but empty is OK)
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_set_value(heim_db_t db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ heim_string_t key64 = NULL;
+ int ret;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (value == NULL)
+ /* Use heim_null_t instead of NULL */
+ return heim_db_delete_key(db, table, key, error);
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (heim_get_tid(key) != HEIM_TID_DATA)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("DB keys must be data", "")));
+
+ if (db->plug->setf == NULL)
+ return EBADF;
+
+ if (!db->in_transaction) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ heim_assert(db->in_transaction, "Internal error");
+ ret = heim_db_set_value(db, table, key, value, error);
+ if (ret) {
+ (void) heim_db_rollback(db, NULL);
+ return ret;
+ }
+ return heim_db_commit(db, error);
+ }
+
+ /* Transaction emulation */
+ heim_assert(db->set_keys != NULL, "Internal error");
+ key64 = to_base64(key, error);
+ if (key64 == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (db->ro_tx) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ }
+ ret = heim_path_create(db->set_keys, 29, value, error, table, key64, NULL);
+ if (ret)
+ goto err;
+ heim_path_delete(db->del_keys, error, table, key64, NULL);
+ heim_release(key64);
+
+ return 0;
+
+err:
+ heim_release(key64);
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Could not set a dict value while while "
+ "setting a DB value", "")));
+}
+
+/**
+ * Delete a key and its value from the DB
+ *
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_delete_key(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_string_t key64 = NULL;
+ int ret;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (db->plug->delf == NULL)
+ return EBADF;
+
+ if (!db->in_transaction) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ heim_assert(db->in_transaction, "Internal error");
+ ret = heim_db_delete_key(db, table, key, error);
+ if (ret) {
+ (void) heim_db_rollback(db, NULL);
+ return ret;
+ }
+ return heim_db_commit(db, error);
+ }
+
+ /* Transaction emulation */
+ heim_assert(db->set_keys != NULL, "Internal error");
+ key64 = to_base64(key, error);
+ if (key64 == NULL)
+ return HEIM_ENOMEM(error);
+ if (db->ro_tx) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ }
+ ret = heim_path_create(db->del_keys, 29, heim_number_create(1), error, table, key64, NULL);
+ if (ret)
+ goto err;
+ heim_path_delete(db->set_keys, error, table, key64, NULL);
+ heim_release(key64);
+
+ return 0;
+
+err:
+ heim_release(key64);
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Could not set a dict value while while "
+ "deleting a DB value", "")));
+}
+
+/**
+ * Iterate a callback function over keys and values from a DB.
+ *
+ * @param db Open DB handle
+ * @param iter_data Callback function's private data
+ * @param iter_f Callback function, called once per-key/value pair
+ * @param error Output error object
+ *
+ * @addtogroup heimbase
+ */
+void
+heim_db_iterate_f(heim_db_t db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ if (error != NULL)
+ *error = NULL;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return;
+
+ if (!db->in_transaction)
+ db->plug->iterf(db->db_data, table, iter_data, iter_f, error);
+}
+
+static void
+db_replay_log_table_set_keys_iter(heim_object_t key, heim_object_t value,
+ void *arg)
+{
+ heim_db_t db = arg;
+ heim_data_t k, v;
+
+ if (db->ret)
+ return;
+
+ k = from_base64((heim_string_t)key, &db->error);
+ if (k == NULL) {
+ db->ret = ENOMEM;
+ return;
+ }
+ v = (heim_data_t)value;
+
+ db->ret = db->plug->setf(db->db_data, db->current_table, k, v, &db->error);
+ heim_release(k);
+}
+
+static void
+db_replay_log_table_del_keys_iter(heim_object_t key, heim_object_t value,
+ void *arg)
+{
+ heim_db_t db = arg;
+ heim_data_t k;
+
+ if (db->ret) {
+ db->ret = ENOMEM;
+ return;
+ }
+
+ k = from_base64((heim_string_t)key, &db->error);
+ if (k == NULL)
+ return;
+
+ db->ret = db->plug->delf(db->db_data, db->current_table, k, &db->error);
+ heim_release(k);
+}
+
+static void
+db_replay_log_set_keys_iter(heim_object_t table, heim_object_t table_dict,
+ void *arg)
+{
+ heim_db_t db = arg;
+
+ if (db->ret)
+ return;
+
+ db->current_table = table;
+ heim_dict_iterate_f(table_dict, db, db_replay_log_table_set_keys_iter);
+}
+
+static void
+db_replay_log_del_keys_iter(heim_object_t table, heim_object_t table_dict,
+ void *arg)
+{
+ heim_db_t db = arg;
+
+ if (db->ret)
+ return;
+
+ db->current_table = table;
+ heim_dict_iterate_f(table_dict, db, db_replay_log_table_del_keys_iter);
+}
+
+static int
+db_do_log_actions(heim_db_t db, heim_error_t *error)
+{
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ db->ret = 0;
+ db->error = NULL;
+ if (db->set_keys != NULL)
+ heim_dict_iterate_f(db->set_keys, db, db_replay_log_set_keys_iter);
+ if (db->del_keys != NULL)
+ heim_dict_iterate_f(db->del_keys, db, db_replay_log_del_keys_iter);
+
+ ret = db->ret;
+ db->ret = 0;
+ if (error && db->error) {
+ *error = db->error;
+ db->error = NULL;
+ } else {
+ heim_release(db->error);
+ db->error = NULL;
+ }
+ return ret;
+}
+
+static int
+db_replay_log(heim_db_t db, heim_error_t *error)
+{
+ int ret;
+ heim_string_t journal_fname = NULL;
+ heim_object_t journal;
+ size_t len;
+
+ heim_assert(!db->in_transaction, "DB transaction not open");
+ heim_assert(db->set_keys == NULL && db->set_keys == NULL, "DB transaction not open");
+
+ if (error)
+ *error = NULL;
+
+ if (db->options == NULL)
+ return 0;
+
+ journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename"));
+ if (journal_fname == NULL)
+ return 0;
+
+ ret = read_json(heim_string_get_utf8(journal_fname), &journal, error);
+ if (ret == ENOENT) {
+ heim_release(journal_fname);
+ return 0;
+ }
+ if (ret == 0 && journal == NULL) {
+ heim_release(journal_fname);
+ return 0;
+ }
+ if (ret != 0) {
+ heim_release(journal_fname);
+ return ret;
+ }
+
+ if (heim_get_tid(journal) != HEIM_TID_ARRAY) {
+ heim_release(journal_fname);
+ return HEIM_ERROR(error, EINVAL,
+ (ret, N_("Invalid journal contents; delete journal",
+ "")));
+ }
+
+ len = heim_array_get_length(journal);
+
+ if (len > 0)
+ db->set_keys = heim_array_get_value(journal, 0);
+ if (len > 1)
+ db->del_keys = heim_array_get_value(journal, 1);
+ ret = db_do_log_actions(db, error);
+ if (ret) {
+ heim_release(journal_fname);
+ return ret;
+ }
+
+ /* Truncate replay log and we're done */
+ ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error);
+ heim_release(journal_fname);
+ if (ret)
+ return ret;
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+
+ return 0;
+}
+
+static
+heim_string_t to_base64(heim_data_t data, heim_error_t *error)
+{
+ char *b64 = NULL;
+ heim_string_t s = NULL;
+ const heim_octet_string *d;
+ int ret;
+
+ d = heim_data_get_data(data);
+ ret = rk_base64_encode(d->data, d->length, &b64);
+ if (ret < 0 || b64 == NULL)
+ goto enomem;
+ s = heim_string_ref_create(b64, free);
+ if (s == NULL)
+ goto enomem;
+ return s;
+
+enomem:
+ free(b64);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+}
+
+static
+heim_data_t from_base64(heim_string_t s, heim_error_t *error)
+{
+ void *buf;
+ size_t len;
+ heim_data_t d;
+
+ buf = malloc(strlen(heim_string_get_utf8(s)));
+ if (buf == NULL)
+ goto enomem;
+
+ len = rk_base64_decode(heim_string_get_utf8(s), buf);
+ d = heim_data_ref_create(buf, len, free);
+ if (d == NULL)
+ goto enomem;
+ return d;
+
+enomem:
+ free(buf);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+}
+
+
+static int
+open_file(const char *dbname, int for_write, int excl, int *fd_out, heim_error_t *error)
+{
+#ifdef WIN32
+ HANDLE hFile;
+ int ret = 0;
+
+ if (fd_out)
+ *fd_out = -1;
+
+ if (for_write)
+ hFile = CreateFile(dbname, GENERIC_WRITE | GENERIC_READ, 0,
+ NULL, /* we'll close as soon as we read */
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ else
+ hFile = CreateFile(dbname, GENERIC_READ, FILE_SHARE_READ,
+ NULL, /* we'll close as soon as we read */
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ ret = GetLastError();
+ _set_errno(ret); /* CreateFile() does not set errno */
+ goto err;
+ }
+ if (fd_out == NULL) {
+ (void) CloseHandle(hFile);
+ return 0;
+ }
+
+ *fd_out = _open_osfhandle((intptr_t) hFile, 0);
+ if (*fd_out < 0) {
+ ret = errno;
+ (void) CloseHandle(hFile);
+ goto err;
+ }
+
+ /* No need to lock given share deny mode */
+ return 0;
+
+err:
+ if (error != NULL) {
+ char *s = NULL;
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ 0, ret, 0, (LPTSTR) &s, 0, NULL);
+ *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""),
+ dbname, s ? s : "<error formatting error>");
+ LocalFree(s);
+ }
+ return ret;
+#else
+ int ret = 0;
+ int fd;
+
+ if (fd_out)
+ *fd_out = -1;
+
+ if (for_write && excl)
+ fd = open(dbname, O_CREAT | O_EXCL | O_WRONLY, 0600);
+ else if (for_write)
+ fd = open(dbname, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ else
+ fd = open(dbname, O_RDONLY);
+ if (fd < 0) {
+ if (error != NULL)
+ *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""),
+ dbname, strerror(errno));
+ return errno;
+ }
+
+ if (fd_out == NULL) {
+ (void) close(fd);
+ return 0;
+ }
+
+ ret = flock(fd, for_write ? LOCK_EX : LOCK_SH);
+ if (ret == -1) {
+ /* Note that we if O_EXCL we're leaving the [lock] file around */
+ (void) close(fd);
+ return HEIM_ERROR(error, errno,
+ (errno, N_("Could not lock JSON file %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+
+ *fd_out = fd;
+
+ return 0;
+#endif
+}
+
+static int
+read_json(const char *dbname, heim_object_t *out, heim_error_t *error)
+{
+ struct stat st;
+ char *str = NULL;
+ int ret;
+ int fd = -1;
+ ssize_t bytes;
+
+ *out = NULL;
+ ret = open_file(dbname, 0, 0, &fd, error);
+ if (ret)
+ return ret;
+
+ ret = fstat(fd, &st);
+ if (ret == -1) {
+ (void) close(fd);
+ return HEIM_ERROR(error, errno,
+ (ret, N_("Could not stat JSON DB %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+
+ if (st.st_size == 0) {
+ (void) close(fd);
+ return 0;
+ }
+
+ str = malloc(st.st_size + 1);
+ if (str == NULL) {
+ (void) close(fd);
+ return HEIM_ENOMEM(error);
+ }
+
+ bytes = read(fd, str, st.st_size);
+ (void) close(fd);
+ if (bytes != st.st_size) {
+ free(str);
+ if (bytes >= 0)
+ errno = EINVAL; /* ?? */
+ return HEIM_ERROR(error, errno,
+ (ret, N_("Could not read JSON DB %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+ str[st.st_size] = '\0';
+ *out = heim_json_create(str, 10, 0, error);
+ free(str);
+ if (*out == NULL)
+ return (error && *error) ? heim_error_get_code(*error) : EINVAL;
+ return 0;
+}
+
+typedef struct json_db {
+ heim_dict_t dict;
+ heim_string_t dbname;
+ heim_string_t bkpname;
+ int fd;
+ time_t last_read_time;
+ unsigned int read_only:1;
+ unsigned int locked:1;
+ unsigned int locked_needs_unlink:1;
+} *json_db_t;
+
+static int
+json_db_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ json_db_t jsondb;
+ heim_dict_t contents = NULL;
+ heim_string_t dbname_s = NULL;
+ heim_string_t bkpname_s = NULL;
+
+ if (error)
+ *error = NULL;
+ if (dbtype && *dbtype && strcmp(dbtype, "json"))
+ return HEIM_ERROR(error, EINVAL, (EINVAL, N_("Wrong DB type", "")));
+ if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0) {
+ char *ext = strrchr(dbname, '.');
+ char *bkpname;
+ size_t len;
+ int ret;
+
+ if (ext == NULL || strcmp(ext, ".json") != 0)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB files must end in .json",
+ "")));
+
+ if (options) {
+ heim_object_t vc, ve, vt;
+
+ vc = heim_dict_get_value(options, HSTR("create"));
+ ve = heim_dict_get_value(options, HSTR("exclusive"));
+ vt = heim_dict_get_value(options, HSTR("truncate"));
+ if (vc && vt) {
+ ret = open_file(dbname, 1, ve ? 1 : 0, NULL, error);
+ if (ret)
+ return ret;
+ } else if (vc || ve || vt) {
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("Invalid JSON DB open options",
+ "")));
+ }
+ /*
+ * We don't want cloned handles to truncate the DB, eh?
+ *
+ * We should really just create a copy of the options dict
+ * rather than modify the caller's! But for that it'd be
+ * nicer to have copy utilities in heimbase, something like
+ * this:
+ *
+ * heim_object_t heim_copy(heim_object_t src, int depth,
+ * heim_error_t *error);
+ *
+ * so that options = heim_copy(options, 1); means copy the
+ * dict but nothing else (whereas depth == 0 would mean
+ * heim_retain(), and depth > 1 would be copy that many
+ * levels).
+ */
+ heim_dict_delete_key(options, HSTR("create"));
+ heim_dict_delete_key(options, HSTR("exclusive"));
+ heim_dict_delete_key(options, HSTR("truncate"));
+ }
+ dbname_s = heim_string_create(dbname);
+ if (dbname_s == NULL)
+ return HEIM_ENOMEM(error);
+
+ len = snprintf(NULL, 0, "%s~", dbname);
+ bkpname = malloc(len + 2);
+ if (bkpname == NULL) {
+ heim_release(dbname_s);
+ return HEIM_ENOMEM(error);
+ }
+ (void) snprintf(bkpname, len + 1, "%s~", dbname);
+ bkpname_s = heim_string_create(bkpname);
+ free(bkpname);
+ if (bkpname_s == NULL) {
+ heim_release(dbname_s);
+ return HEIM_ENOMEM(error);
+ }
+
+ ret = read_json(dbname, (heim_object_t *)&contents, error);
+ if (ret) {
+ heim_release(bkpname_s);
+ heim_release(dbname_s);
+ return ret;
+ }
+
+ if (contents != NULL && heim_get_tid(contents) != HEIM_TID_DICT) {
+ heim_release(bkpname_s);
+ heim_release(dbname_s);
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB contents not valid JSON",
+ "")));
+ }
+ }
+
+ jsondb = heim_alloc(sizeof (*jsondb), "json_db", NULL);
+ if (jsondb == NULL) {
+ heim_release(contents);
+ heim_release(dbname_s);
+ heim_release(bkpname_s);
+ return ENOMEM;
+ }
+
+ jsondb->last_read_time = time(NULL);
+ jsondb->fd = -1;
+ jsondb->dbname = dbname_s;
+ jsondb->bkpname = bkpname_s;
+ jsondb->read_only = 0;
+
+ if (contents != NULL)
+ jsondb->dict = contents;
+ else {
+ jsondb->dict = heim_dict_create(29);
+ if (jsondb->dict == NULL) {
+ heim_release(jsondb);
+ return ENOMEM;
+ }
+ }
+
+ *db = jsondb;
+ return 0;
+}
+
+static int
+json_db_close(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+
+ if (error)
+ *error = NULL;
+ if (jsondb->fd > -1)
+ (void) close(jsondb->fd);
+ jsondb->fd = -1;
+ heim_release(jsondb->dbname);
+ heim_release(jsondb->bkpname);
+ heim_release(jsondb->dict);
+ heim_release(jsondb);
+ return 0;
+}
+
+static int
+json_db_lock(void *db, int read_only, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ int ret;
+
+ heim_assert(jsondb->fd == -1 || (jsondb->read_only && !read_only),
+ "DB locks are not recursive");
+
+ jsondb->read_only = read_only ? 1 : 0;
+ if (jsondb->fd > -1)
+ return 0;
+
+ ret = open_file(heim_string_get_utf8(jsondb->bkpname), 1, 1, &jsondb->fd, error);
+ if (ret == 0) {
+ jsondb->locked_needs_unlink = 1;
+ jsondb->locked = 1;
+ }
+ return ret;
+}
+
+static int
+json_db_unlock(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ int ret = 0;
+
+ heim_assert(jsondb->locked, "DB not locked when unlock attempted");
+ if (jsondb->fd > -1)
+ ret = close(jsondb->fd);
+ jsondb->fd = -1;
+ jsondb->read_only = 0;
+ jsondb->locked = 0;
+ if (jsondb->locked_needs_unlink)
+ unlink(heim_string_get_utf8(jsondb->bkpname));
+ jsondb->locked_needs_unlink = 0;
+ return ret;
+}
+
+static int
+json_db_sync(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ size_t len, bytes;
+ heim_error_t e;
+ heim_string_t json;
+ const char *json_text = NULL;
+ int ret = 0;
+ int fd = -1;
+#ifdef WIN32
+ int tries = 3;
+#endif
+
+ heim_assert(jsondb->fd > -1, "DB not locked when sync attempted");
+
+ json = heim_json_copy_serialize(jsondb->dict, 0, &e);
+ if (json == NULL) {
+ if (error)
+ *error = e;
+ else
+ heim_release(e);
+ return heim_error_get_code(e);
+ }
+
+ json_text = heim_string_get_utf8(json);
+ len = strlen(json_text);
+ errno = 0;
+
+#ifdef WIN32
+ while (tries--) {
+ ret = open_file(heim_string_get_utf8(jsondb->dbname), 1, 0, &fd, error);
+ if (ret == 0)
+ break;
+ sleep(1);
+ }
+ if (ret) {
+ heim_release(json);
+ return ret;
+ }
+#else
+ fd = jsondb->fd;
+#endif /* WIN32 */
+
+ bytes = write(fd, json_text, len);
+ heim_release(json);
+ if (bytes != len)
+ return errno ? errno : EIO;
+ ret = fsync(fd);
+ if (ret)
+ return ret;
+
+#ifdef WIN32
+ ret = close(fd);
+ if (ret)
+ return GetLastError();
+#else
+ ret = rename(heim_string_get_utf8(jsondb->bkpname), heim_string_get_utf8(jsondb->dbname));
+ if (ret == 0) {
+ jsondb->locked_needs_unlink = 0;
+ return 0;
+ }
+#endif /* WIN32 */
+
+ return errno;
+}
+
+static heim_data_t
+json_db_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+ struct stat st;
+ heim_data_t result;
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length) {
+ HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB requires keys that are actually "
+ "strings", "")));
+ return NULL;
+ }
+
+ if (stat(heim_string_get_utf8(jsondb->dbname), &st) == -1) {
+ HEIM_ERROR(error, errno,
+ (errno, N_("Could not stat JSON DB file", "")));
+ return NULL;
+ }
+
+ if (st.st_mtime > jsondb->last_read_time ||
+ st.st_ctime > jsondb->last_read_time) {
+ heim_dict_t contents = NULL;
+ int ret;
+
+ /* Ignore file is gone (ENOENT) */
+ ret = read_json(heim_string_get_utf8(jsondb->dbname),
+ (heim_object_t *)&contents, error);
+ if (ret)
+ return NULL;
+ if (contents == NULL)
+ contents = heim_dict_create(29);
+ heim_release(jsondb->dict);
+ jsondb->dict = contents;
+ jsondb->last_read_time = time(NULL);
+ }
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL) {
+ (void) HEIM_ENOMEM(error);
+ return NULL;
+ }
+
+ result = heim_path_copy(jsondb->dict, error, table, key_string, NULL);
+ heim_release(key_string);
+ return result;
+}
+
+static int
+json_db_set_value(void *db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL,
+ N_("JSON DB requires keys that are actually strings",
+ "")));
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (table == NULL)
+ table = HSTR("");
+
+ ret = heim_path_create(jsondb->dict, 29, value, error, table, key_string, NULL);
+ heim_release(key_string);
+ return ret;
+}
+
+static int
+json_db_del_key(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL,
+ N_("JSON DB requires keys that are actually strings",
+ "")));
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (table == NULL)
+ table = HSTR("");
+
+ heim_path_delete(jsondb->dict, error, table, key_string, NULL);
+ heim_release(key_string);
+ return 0;
+}
+
+struct json_db_iter_ctx {
+ heim_db_iterator_f_t iter_f;
+ void *iter_ctx;
+};
+
+static void json_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct json_db_iter_ctx *ctx = arg;
+ const char *key_string;
+ heim_data_t key_data;
+
+ key_string = heim_string_get_utf8((heim_string_t)key);
+ key_data = heim_data_ref_create(key_string, strlen(key_string), NULL);
+ ctx->iter_f(key_data, (heim_object_t)value, ctx->iter_ctx);
+ heim_release(key_data);
+}
+
+static void
+json_db_iter(void *db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ struct json_db_iter_ctx ctx;
+ heim_dict_t table_dict;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ table_dict = heim_dict_get_value(jsondb->dict, table);
+ if (table_dict == NULL)
+ return;
+
+ ctx.iter_ctx = iter_data;
+ ctx.iter_f = iter_f;
+
+ heim_dict_iterate_f(table_dict, &ctx, json_db_iter_f);
+}
+
+static struct heim_db_type json_dbt = {
+ 1, json_db_open, NULL, json_db_close,
+ json_db_lock, json_db_unlock, json_db_sync,
+ NULL, NULL, NULL,
+ json_db_copy_value, json_db_set_value,
+ json_db_del_key, json_db_iter
+};
+
diff --git a/lib/base/dict.c b/lib/base/dict.c
new file mode 100644
index 000000000000..8d73846b2bbb
--- /dev/null
+++ b/lib/base/dict.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2002, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+
+struct hashentry {
+ struct hashentry **prev;
+ struct hashentry *next;
+ heim_object_t key;
+ heim_object_t value;
+};
+
+struct heim_dict_data {
+ size_t size;
+ struct hashentry **tab;
+};
+
+static void
+dict_dealloc(void *ptr)
+{
+ heim_dict_t dict = ptr;
+ struct hashentry **h, *g, *i;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h) {
+ for (g = h[0]; g; g = i) {
+ i = g->next;
+ heim_release(g->key);
+ heim_release(g->value);
+ free(g);
+ }
+ }
+ free(dict->tab);
+}
+
+struct heim_type_data dict_object = {
+ HEIM_TID_DICT,
+ "dict-object",
+ NULL,
+ dict_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static size_t
+isprime(size_t p)
+{
+ size_t q, i;
+
+ for(i = 2 ; i < p; i++) {
+ q = p / i;
+
+ if (i * q == p)
+ return 0;
+ if (i * i > p)
+ return 1;
+ }
+ return 1;
+}
+
+static size_t
+findprime(size_t p)
+{
+ if (p % 2 == 0)
+ p++;
+
+ while (isprime(p) == 0)
+ p += 2;
+
+ return p;
+}
+
+/**
+ * Allocate an array
+ *
+ * @return A new allocated array, free with heim_release()
+ */
+
+heim_dict_t
+heim_dict_create(size_t size)
+{
+ heim_dict_t dict;
+
+ dict = _heim_alloc_object(&dict_object, sizeof(*dict));
+
+ dict->size = findprime(size);
+ if (dict->size == 0) {
+ heim_release(dict);
+ return NULL;
+ }
+
+ dict->tab = calloc(dict->size, sizeof(dict->tab[0]));
+ if (dict->tab == NULL) {
+ dict->size = 0;
+ heim_release(dict);
+ return NULL;
+ }
+
+ return dict;
+}
+
+/**
+ * Get type id of an dict
+ *
+ * @return the type id
+ */
+
+heim_tid_t
+heim_dict_get_type_id(void)
+{
+ return HEIM_TID_DICT;
+}
+
+/* Intern search function */
+
+static struct hashentry *
+_search(heim_dict_t dict, heim_object_t ptr)
+{
+ unsigned long v = heim_get_hash(ptr);
+ struct hashentry *p;
+
+ for (p = dict->tab[v % dict->size]; p != NULL; p = p->next)
+ if (heim_cmp(ptr, p->key) == 0)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * Search for element in hash table
+ *
+ * @value dict the dict to search in
+ * @value key the key to search for
+ *
+ * @return a not-retained copy of the value for key or NULL if not found
+ */
+
+heim_object_t
+heim_dict_get_value(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *p;
+ p = _search(dict, key);
+ if (p == NULL)
+ return NULL;
+
+ return p->value;
+}
+
+/**
+ * Search for element in hash table
+ *
+ * @value dict the dict to search in
+ * @value key the key to search for
+ *
+ * @return a retained copy of the value for key or NULL if not found
+ */
+
+heim_object_t
+heim_dict_copy_value(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *p;
+ p = _search(dict, key);
+ if (p == NULL)
+ return NULL;
+
+ return heim_retain(p->value);
+}
+
+/**
+ * Add key and value to dict
+ *
+ * @value dict the dict to add too
+ * @value key the key to add
+ * @value value the value to add
+ *
+ * @return 0 if added, errno if not
+ */
+
+int
+heim_dict_set_value(heim_dict_t dict, heim_object_t key, heim_object_t value)
+{
+ struct hashentry **tabptr, *h;
+
+ h = _search(dict, key);
+ if (h) {
+ heim_release(h->value);
+ h->value = heim_retain(value);
+ } else {
+ unsigned long v;
+
+ h = malloc(sizeof(*h));
+ if (h == NULL)
+ return ENOMEM;
+
+ h->key = heim_retain(key);
+ h->value = heim_retain(value);
+
+ v = heim_get_hash(key);
+
+ tabptr = &dict->tab[v % dict->size];
+ h->next = *tabptr;
+ *tabptr = h;
+ h->prev = tabptr;
+ if (h->next)
+ h->next->prev = &h->next;
+ }
+
+ return 0;
+}
+
+/**
+ * Delete element with key key
+ *
+ * @value dict the dict to delete from
+ * @value key the key to delete
+ */
+
+void
+heim_dict_delete_key(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *h = _search(dict, key);
+
+ if (h == NULL)
+ return;
+
+ heim_release(h->key);
+ heim_release(h->value);
+
+ if ((*(h->prev) = h->next) != NULL)
+ h->next->prev = h->prev;
+
+ free(h);
+}
+
+/**
+ * Do something for each element
+ *
+ * @value dict the dict to interate over
+ * @value func the function to search for
+ * @value arg argument to func
+ */
+
+void
+heim_dict_iterate_f(heim_dict_t dict, void *arg, heim_dict_iterator_f_t func)
+{
+ struct hashentry **h, *g;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
+ for (g = *h; g; g = g->next)
+ func(g->key, g->value, arg);
+}
+
+#ifdef __BLOCKS__
+/**
+ * Do something for each element
+ *
+ * @value dict the dict to interate over
+ * @value func the function to search for
+ */
+
+void
+heim_dict_iterate(heim_dict_t dict, void (^func)(heim_object_t, heim_object_t))
+{
+ struct hashentry **h, *g;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
+ for (g = *h; g; g = g->next)
+ func(g->key, g->value);
+}
+#endif
diff --git a/lib/base/dll.c b/lib/base/dll.c
new file mode 100644
index 000000000000..31017a01191b
--- /dev/null
+++ b/lib/base/dll.c
@@ -0,0 +1,324 @@
+/***********************************************************************
+ * Copyright (c) 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 HOLDER 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.
+ *
+ **********************************************************************/
+
+/*
+ * This is an implementation of thread-specific storage with
+ * destructors. WIN32 doesn't quite have this. Instead it has
+ * DllMain(), an entry point in every DLL that gets called to notify the
+ * DLL of thread/process "attach"/"detach" events.
+ *
+ * We use __thread (or __declspec(thread)) for the thread-local itself
+ * and DllMain() DLL_THREAD_DETACH events to drive destruction of
+ * thread-local values.
+ *
+ * When building in maintainer mode on non-Windows pthread systems this
+ * uses a single pthread key instead to implement multiple keys. This
+ * keeps the code from rotting when modified by non-Windows developers.
+ */
+
+#include "baselocl.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef HEIM_WIN32_TLS
+#include <assert.h>
+#include <err.h>
+#include <heim_threads.h>
+
+#ifndef WIN32
+#include <pthread.h>
+#endif
+
+/* Logical array of keys that grows lock-lessly */
+typedef struct tls_keys tls_keys;
+struct tls_keys {
+ void (**keys_dtors)(void *); /* array of destructors */
+ size_t keys_start_idx; /* index of first destructor */
+ size_t keys_num;
+ tls_keys *keys_next;
+};
+
+/*
+ * Well, not quite locklessly. We need synchronization primitives to do
+ * this locklessly. An atomic CAS will do.
+ */
+static HEIMDAL_MUTEX tls_key_defs_lock = HEIMDAL_MUTEX_INITIALIZER;
+static tls_keys *tls_key_defs;
+
+/* Logical array of values (per-thread; no locking needed here) */
+struct tls_values {
+ void **values; /* realloc()ed */
+ size_t values_num;
+};
+
+static HEIMDAL_THREAD_LOCAL struct tls_values values;
+
+#define DEAD_KEY ((void *)8)
+
+void
+heim_w32_service_thread_detach(void *unused)
+{
+ tls_keys *key_defs;
+ void (*dtor)(void*);
+ size_t i;
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = tls_key_defs;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ if (key_defs == NULL)
+ return;
+
+ for (i = 0; i < values.values_num; i++) {
+ assert(i >= key_defs->keys_start_idx);
+ if (i >= key_defs->keys_start_idx + key_defs->keys_num) {
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = key_defs->keys_next;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ assert(key_defs != NULL);
+ assert(i >= key_defs->keys_start_idx);
+ assert(i < key_defs->keys_start_idx + key_defs->keys_num);
+ }
+ dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx];
+ if (values.values[i] != NULL && dtor != NULL && dtor != DEAD_KEY)
+ dtor(values.values[i]);
+ values.values[i] = NULL;
+ }
+}
+
+#if !defined(WIN32)
+static pthread_key_t pt_key;
+pthread_once_t pt_once = PTHREAD_ONCE_INIT;
+
+static void
+atexit_del_tls_for_thread(void)
+{
+ heim_w32_service_thread_detach(NULL);
+}
+
+static void
+create_pt_key(void)
+{
+ int ret;
+
+ /* The main thread may not execute TLS destructors */
+ atexit(atexit_del_tls_for_thread);
+ ret = pthread_key_create(&pt_key, heim_w32_service_thread_detach);
+ if (ret != 0)
+ err(1, "pthread_key_create() failed");
+}
+
+#endif
+
+int
+heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
+{
+ tls_keys *key_defs, *new_key_defs;
+ size_t i, k;
+ int ret = ENOMEM;
+
+#if !defined(WIN32)
+ (void) pthread_once(&pt_once, create_pt_key);
+ (void) pthread_setspecific(pt_key, DEAD_KEY);
+#endif
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ if (tls_key_defs == NULL) {
+ /* First key */
+ new_key_defs = calloc(1, sizeof(*new_key_defs));
+ if (new_key_defs == NULL) {
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return ENOMEM;
+ }
+ new_key_defs->keys_num = 8;
+ new_key_defs->keys_dtors = calloc(new_key_defs->keys_num,
+ sizeof(*new_key_defs->keys_dtors));
+ if (new_key_defs->keys_dtors == NULL) {
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ free(new_key_defs);
+ return ENOMEM;
+ }
+ tls_key_defs = new_key_defs;
+ new_key_defs->keys_dtors[0] = dtor;
+ for (i = 1; i < new_key_defs->keys_num; i++)
+ new_key_defs->keys_dtors[i] = NULL;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return 0;
+ }
+
+ for (key_defs = tls_key_defs;
+ key_defs != NULL;
+ key_defs = key_defs->keys_next) {
+ k = key_defs->keys_start_idx;
+ for (i = 0; i < key_defs->keys_num; i++, k++) {
+ if (key_defs->keys_dtors[i] == NULL) {
+ /* Found free slot; use it */
+ key_defs->keys_dtors[i] = dtor;
+ *key = k;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return 0;
+ }
+ }
+ if (key_defs->keys_next != NULL)
+ continue;
+
+ /* Grow the registration array */
+ /* XXX DRY */
+ new_key_defs = calloc(1, sizeof(*new_key_defs));
+ if (new_key_defs == NULL)
+ break;
+
+ new_key_defs->keys_dtors =
+ calloc(key_defs->keys_num + key_defs->keys_num / 2,
+ sizeof(*new_key_defs->keys_dtors));
+ if (new_key_defs->keys_dtors == NULL) {
+ free(new_key_defs);
+ break;
+ }
+ new_key_defs->keys_start_idx = key_defs->keys_start_idx +
+ key_defs->keys_num;
+ new_key_defs->keys_num = key_defs->keys_num + key_defs->keys_num / 2;
+ new_key_defs->keys_dtors[i] = dtor;
+ for (i = 1; i < new_key_defs->keys_num; i++)
+ new_key_defs->keys_dtors[i] = NULL;
+ key_defs->keys_next = new_key_defs;
+ ret = 0;
+ break;
+ }
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return ret;
+}
+
+static void
+key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
+ size_t *dtor_idx, void (**dtor)(void *))
+{
+ tls_keys *key_defs;
+
+ if (kd != NULL)
+ *kd = NULL;
+ if (dtor_idx != NULL)
+ *dtor_idx = 0;
+ if (dtor != NULL)
+ *dtor = NULL;
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = tls_key_defs;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ while (key_defs != NULL) {
+ if (key >= key_defs->keys_start_idx &&
+ key < key_defs->keys_start_idx + key_defs->keys_num) {
+ if (kd != NULL)
+ *kd = key_defs;
+ if (dtor_idx != NULL)
+ *dtor_idx = key - key_defs->keys_start_idx;
+ if (dtor != NULL)
+ *dtor = key_defs->keys_dtors[key - key_defs->keys_start_idx];
+ return;
+ }
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = key_defs->keys_next;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ assert(key_defs != NULL);
+ assert(key >= key_defs->keys_start_idx);
+ }
+}
+
+int
+heim_w32_delete_key(HEIM_PRIV_thread_key key)
+{
+ tls_keys *key_defs;
+ size_t dtor_idx;
+
+ key_lookup(key, &key_defs, &dtor_idx, NULL);
+ if (key_defs == NULL)
+ return EINVAL;
+ key_defs->keys_dtors[dtor_idx] = DEAD_KEY;
+ return 0;
+}
+
+int
+heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
+{
+ void **new_values;
+ size_t new_num;
+ void (*dtor)(void *);
+ size_t i;
+
+#if !defined(WIN32)
+ (void) pthread_setspecific(pt_key, DEAD_KEY);
+#endif
+
+ key_lookup(key, NULL, NULL, &dtor);
+ if (dtor == NULL)
+ return EINVAL;
+
+ if (key >= values.values_num) {
+ if (values.values_num == 0) {
+ values.values = NULL;
+ new_num = 8;
+ } else {
+ new_num = (values.values_num + values.values_num / 2);
+ }
+ new_values = realloc(values.values, sizeof(void *) * new_num);
+ if (new_values == NULL)
+ return ENOMEM;
+ for (i = values.values_num; i < new_num; i++)
+ new_values[i] = NULL;
+ values.values = new_values;
+ values.values_num = new_num;
+ }
+
+ assert(key < values.values_num);
+
+ if (values.values[key] != NULL && dtor != NULL && dtor != DEAD_KEY)
+ dtor(values.values[key]);
+
+ values.values[key] = value;
+ return 0;
+}
+
+void *
+heim_w32_getspecific(HEIM_PRIV_thread_key key)
+{
+ if (key >= values.values_num)
+ return NULL;
+ return values.values[key];
+}
+
+#else
+static char dummy;
+#endif /* HEIM_WIN32_TLS */
diff --git a/lib/base/error.c b/lib/base/error.c
new file mode 100644
index 000000000000..a1bbfa8a133f
--- /dev/null
+++ b/lib/base/error.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+
+struct heim_error {
+ int error_code;
+ heim_string_t msg;
+ struct heim_error *next;
+};
+
+static void
+error_dealloc(void *ptr)
+{
+ struct heim_error *p = ptr;
+ heim_release(p->msg);
+ heim_release(p->next);
+}
+
+static int
+error_cmp(void *a, void *b)
+{
+ struct heim_error *ap = a, *bp = b;
+ if (ap->error_code == ap->error_code)
+ return ap->error_code - ap->error_code;
+ return heim_cmp(ap->msg, bp->msg);
+}
+
+static unsigned long
+error_hash(void *ptr)
+{
+ struct heim_error *p = ptr;
+ return p->error_code;
+}
+
+struct heim_type_data _heim_error_object = {
+ HEIM_TID_ERROR,
+ "error-object",
+ NULL,
+ error_dealloc,
+ NULL,
+ error_cmp,
+ error_hash,
+ NULL
+};
+
+heim_error_t
+heim_error_create_enomem(void)
+{
+ /* This is an immediate object; see heim_number_create() */
+ return (heim_error_t)heim_number_create(ENOMEM);
+}
+
+
+void
+heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
+{
+ if (error) {
+ va_list ap;
+ va_start(ap, fmt);
+ *error = heim_error_createv(error_code, fmt, ap);
+ va_end(ap);
+ }
+}
+
+heim_error_t
+heim_error_create(int error_code, const char *fmt, ...)
+{
+ heim_error_t e;
+ va_list ap;
+
+ va_start(ap, fmt);
+ e = heim_error_createv(error_code, fmt, ap);
+ va_end(ap);
+
+ return e;
+}
+
+heim_error_t
+heim_error_createv(int error_code, const char *fmt, va_list ap)
+{
+ heim_error_t e;
+ char *str;
+ int len;
+ int save_errno = errno;
+
+ str = malloc(1024);
+ errno = save_errno;
+ if (str == NULL)
+ return heim_error_create_enomem();
+ len = vsnprintf(str, 1024, fmt, ap);
+ errno = save_errno;
+ if (len < 0) {
+ free(str);
+ return NULL; /* XXX We should have a special heim_error_t for this */
+ }
+
+ e = _heim_alloc_object(&_heim_error_object, sizeof(struct heim_error));
+ if (e) {
+ e->msg = heim_string_create(str);
+ e->error_code = error_code;
+ }
+ free(str);
+
+ errno = save_errno;
+ return e;
+}
+
+heim_string_t
+heim_error_copy_string(heim_error_t error)
+{
+ if (heim_get_tid(error) != HEIM_TID_ERROR) {
+ if (heim_get_tid(error) == heim_number_get_type_id())
+ return __heim_string_constant(strerror(heim_number_get_int((heim_number_t)error)));
+ heim_abort("invalid heim_error_t");
+ }
+ /* XXX concat all strings */
+ return heim_retain(error->msg);
+}
+
+int
+heim_error_get_code(heim_error_t error)
+{
+ if (error == NULL)
+ return -1;
+ if (heim_get_tid(error) != HEIM_TID_ERROR) {
+ if (heim_get_tid(error) == heim_number_get_type_id())
+ return heim_number_get_int((heim_number_t)error);
+ heim_abort("invalid heim_error_t");
+ }
+ return error->error_code;
+}
+
+heim_error_t
+heim_error_append(heim_error_t top, heim_error_t append)
+{
+ if (heim_get_tid(top) != HEIM_TID_ERROR) {
+ if (heim_get_tid(top) == heim_number_get_type_id())
+ return top;
+ heim_abort("invalid heim_error_t");
+ }
+ if (top->next)
+ heim_release(top->next);
+ top->next = heim_retain(append);
+ return top;
+}
diff --git a/lib/base/heimbase.c b/lib/base/heimbase.c
new file mode 100644
index 000000000000..6ad6ab258f09
--- /dev/null
+++ b/lib/base/heimbase.c
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+#include <syslog.h>
+
+static heim_base_atomic_type tidglobal = HEIM_TID_USER;
+
+struct heim_base {
+ heim_type_t isa;
+ heim_base_atomic_type ref_cnt;
+ HEIM_TAILQ_ENTRY(heim_base) autorel;
+ heim_auto_release_t autorelpool;
+ uintptr_t isaextra[3];
+};
+
+/* specialized version of base */
+struct heim_base_mem {
+ heim_type_t isa;
+ heim_base_atomic_type ref_cnt;
+ HEIM_TAILQ_ENTRY(heim_base) autorel;
+ heim_auto_release_t autorelpool;
+ const char *name;
+ void (*dealloc)(void *);
+ uintptr_t isaextra[1];
+};
+
+#define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1)
+#define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1))
+
+#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX
+HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
+#endif
+
+/*
+ * Auto release structure
+ */
+
+struct heim_auto_release {
+ HEIM_TAILQ_HEAD(, heim_base) pool;
+ HEIMDAL_MUTEX pool_mutex;
+ struct heim_auto_release *parent;
+};
+
+
+/**
+ * Retain object (i.e., take a reference)
+ *
+ * @param object to be released, NULL is ok
+ *
+ * @return the same object as passed in
+ */
+
+void *
+heim_retain(void *ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return ptr;
+
+ if (p->ref_cnt == heim_base_atomic_max)
+ return ptr;
+
+ if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
+ heim_abort("resurection");
+ return ptr;
+}
+
+/**
+ * Release object, free if reference count reaches zero
+ *
+ * @param object to be released
+ */
+
+void
+heim_release(void *ptr)
+{
+ heim_base_atomic_type old;
+ struct heim_base *p = PTR2BASE(ptr);
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return;
+
+ if (p->ref_cnt == heim_base_atomic_max)
+ return;
+
+ old = heim_base_atomic_dec(&p->ref_cnt) + 1;
+
+ if (old > 1)
+ return;
+
+ if (old == 1) {
+ heim_auto_release_t ar = p->autorelpool;
+ /* remove from autorel pool list */
+ if (ar) {
+ p->autorelpool = NULL;
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+ }
+ if (p->isa->dealloc)
+ p->isa->dealloc(ptr);
+ free(p);
+ } else
+ heim_abort("over release");
+}
+
+/**
+ * If used require wrapped in autorelease pool
+ */
+
+heim_string_t
+heim_description(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ if (p->isa->desc == NULL)
+ return heim_auto_release(heim_string_ref_create(p->isa->name, NULL));
+ return heim_auto_release(p->isa->desc(ptr));
+}
+
+
+void
+_heim_make_permanent(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ p->ref_cnt = heim_base_atomic_max;
+}
+
+
+static heim_type_t tagged_isa[9] = {
+ &_heim_number_object,
+ &_heim_null_object,
+ &_heim_bool_object,
+
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_type_t
+_heim_get_isa(heim_object_t ptr)
+{
+ struct heim_base *p;
+ if (heim_base_is_tagged(ptr)) {
+ if (heim_base_is_tagged_object(ptr))
+ return tagged_isa[heim_base_tagged_object_tid(ptr)];
+ heim_abort("not a supported tagged type");
+ }
+ p = PTR2BASE(ptr);
+ return p->isa;
+}
+
+/**
+ * Get type ID of object
+ *
+ * @param object object to get type id of
+ *
+ * @return type id of object
+ */
+
+heim_tid_t
+heim_get_tid(heim_object_t ptr)
+{
+ heim_type_t isa = _heim_get_isa(ptr);
+ return isa->tid;
+}
+
+/**
+ * Get hash value of object
+ *
+ * @param object object to get hash value for
+ *
+ * @return a hash value
+ */
+
+unsigned long
+heim_get_hash(heim_object_t ptr)
+{
+ heim_type_t isa = _heim_get_isa(ptr);
+ if (isa->hash)
+ return isa->hash(ptr);
+ return (unsigned long)ptr;
+}
+
+/**
+ * Compare two objects, returns 0 if equal, can use used for qsort()
+ * and friends.
+ *
+ * @param a first object to compare
+ * @param b first object to compare
+ *
+ * @return 0 if objects are equal
+ */
+
+int
+heim_cmp(heim_object_t a, heim_object_t b)
+{
+ heim_tid_t ta, tb;
+ heim_type_t isa;
+
+ ta = heim_get_tid(a);
+ tb = heim_get_tid(b);
+
+ if (ta != tb)
+ return ta - tb;
+
+ isa = _heim_get_isa(a);
+
+ if (isa->cmp)
+ return isa->cmp(a, b);
+
+ return (uintptr_t)a - (uintptr_t)b;
+}
+
+/*
+ * Private - allocates an memory object
+ */
+
+static void
+memory_dealloc(void *ptr)
+{
+ struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr);
+ if (p->dealloc)
+ p->dealloc(ptr);
+}
+
+struct heim_type_data memory_object = {
+ HEIM_TID_MEMORY,
+ "memory-object",
+ NULL,
+ memory_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/**
+ * Allocate memory for an object of anonymous type
+ *
+ * @param size size of object to be allocated
+ * @param name name of ad-hoc type
+ * @param dealloc destructor function
+ *
+ * Objects allocated with this interface do not serialize.
+ *
+ * @return allocated object
+ */
+
+void *
+heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
+{
+ /* XXX use posix_memalign */
+
+ struct heim_base_mem *p = calloc(1, size + sizeof(*p));
+ if (p == NULL)
+ return NULL;
+ p->isa = &memory_object;
+ p->ref_cnt = 1;
+ p->name = name;
+ p->dealloc = dealloc;
+ return BASE2PTR(p);
+}
+
+heim_type_t
+_heim_create_type(const char *name,
+ heim_type_init init,
+ heim_type_dealloc dealloc,
+ heim_type_copy copy,
+ heim_type_cmp cmp,
+ heim_type_hash hash,
+ heim_type_description desc)
+{
+ heim_type_t type;
+
+ type = calloc(1, sizeof(*type));
+ if (type == NULL)
+ return NULL;
+
+ type->tid = heim_base_atomic_inc(&tidglobal);
+ type->name = name;
+ type->init = init;
+ type->dealloc = dealloc;
+ type->copy = copy;
+ type->cmp = cmp;
+ type->hash = hash;
+ type->desc = desc;
+
+ return type;
+}
+
+heim_object_t
+_heim_alloc_object(heim_type_t type, size_t size)
+{
+ /* XXX should use posix_memalign */
+ struct heim_base *p = calloc(1, size + sizeof(*p));
+ if (p == NULL)
+ return NULL;
+ p->isa = type;
+ p->ref_cnt = 1;
+
+ return BASE2PTR(p);
+}
+
+void *
+_heim_get_isaextra(heim_object_t ptr, size_t idx)
+{
+ struct heim_base *p = (struct heim_base *)PTR2BASE(ptr);
+
+ heim_assert(ptr != NULL, "internal error");
+ if (p->isa == &memory_object)
+ return NULL;
+ heim_assert(idx < 3, "invalid private heim_base extra data index");
+ return &p->isaextra[idx];
+}
+
+heim_tid_t
+_heim_type_get_tid(heim_type_t type)
+{
+ return type->tid;
+}
+
+#if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
+static pthread_once_t once_arg_key_once = PTHREAD_ONCE_INIT;
+static pthread_key_t once_arg_key;
+
+static void
+once_arg_key_once_init(void)
+{
+ errno = pthread_key_create(&once_arg_key, NULL);
+ if (errno != 0) {
+ fprintf(stderr,
+ "Error: pthread_key_create() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+}
+
+struct once_callback {
+ void (*fn)(void *);
+ void *data;
+};
+
+static void
+once_callback_caller(void)
+{
+ struct once_callback *once_callback = pthread_getspecific(once_arg_key);
+
+ if (once_callback == NULL) {
+ fprintf(stderr, "Error: pthread_once() calls callback on "
+ "different thread?! Cannot continue.\n");
+ abort();
+ }
+ once_callback->fn(once_callback->data);
+}
+#endif
+
+/**
+ * Call func once and only once
+ *
+ * @param once pointer to a heim_base_once_t
+ * @param ctx context passed to func
+ * @param func function to be called
+ */
+
+void
+heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
+{
+#if defined(WIN32)
+ /*
+ * With a libroken wrapper for some CAS function and a libroken yield()
+ * wrapper we could make this the default implementation when we have
+ * neither Grand Central nor POSX threads.
+ *
+ * We could also adapt the double-checked lock pattern with CAS
+ * providing the necessary memory barriers in the absence of
+ * portable explicit memory barrier APIs.
+ */
+ /*
+ * We use CAS operations in large part to provide implied memory
+ * barriers.
+ *
+ * State 0 means that func() has never executed.
+ * State 1 means that func() is executing.
+ * State 2 means that func() has completed execution.
+ */
+ if (InterlockedCompareExchange(once, 1L, 0L) == 0L) {
+ /* State is now 1 */
+ (*func)(ctx);
+ (void)InterlockedExchange(once, 2L);
+ /* State is now 2 */
+ } else {
+ /*
+ * The InterlockedCompareExchange is being used to fetch
+ * the current state under a full memory barrier. As long
+ * as the current state is 1 continue to spin.
+ */
+ while (InterlockedCompareExchange(once, 2L, 0L) == 1L)
+ SwitchToThread();
+ }
+#elif defined(HAVE_DISPATCH_DISPATCH_H)
+ dispatch_once_f(once, ctx, func);
+#elif defined(ENABLE_PTHREAD_SUPPORT)
+ struct once_callback once_callback;
+
+ once_callback.fn = func;
+ once_callback.data = ctx;
+
+ errno = pthread_once(&once_arg_key_once, once_arg_key_once_init);
+ if (errno != 0) {
+ fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+ errno = pthread_setspecific(once_arg_key, &once_callback);
+ if (errno != 0) {
+ fprintf(stderr,
+ "Error: pthread_setspecific() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+ errno = pthread_once(once, once_callback_caller);
+ if (errno != 0) {
+ fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+#else
+ static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
+ HEIMDAL_MUTEX_lock(&mutex);
+ if (*once == 0) {
+ *once = 1;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ func(ctx);
+ HEIMDAL_MUTEX_lock(&mutex);
+ *once = 2;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ } else if (*once == 2) {
+ HEIMDAL_MUTEX_unlock(&mutex);
+ } else {
+ HEIMDAL_MUTEX_unlock(&mutex);
+ while (1) {
+ struct timeval tv = { 0, 1000 };
+ select(0, NULL, NULL, NULL, &tv);
+ HEIMDAL_MUTEX_lock(&mutex);
+ if (*once == 2)
+ break;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ }
+ HEIMDAL_MUTEX_unlock(&mutex);
+ }
+#endif
+}
+
+/**
+ * Abort and log the failure (using syslog)
+ */
+
+void
+heim_abort(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ heim_abortv(fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * Abort and log the failure (using syslog)
+ */
+
+void
+heim_abortv(const char *fmt, va_list ap)
+{
+ static char str[1024];
+
+ vsnprintf(str, sizeof(str), fmt, ap);
+ syslog(LOG_ERR, "heim_abort: %s", str);
+ abort();
+}
+
+/*
+ *
+ */
+
+static int ar_created = 0;
+static HEIMDAL_thread_key ar_key;
+
+struct ar_tls {
+ struct heim_auto_release *head;
+ struct heim_auto_release *current;
+ HEIMDAL_MUTEX tls_mutex;
+};
+
+static void
+ar_tls_delete(void *ptr)
+{
+ struct ar_tls *tls = ptr;
+ heim_auto_release_t next = NULL;
+
+ if (tls == NULL)
+ return;
+ for (; tls->current != NULL; tls->current = next) {
+ next = tls->current->parent;
+ heim_release(tls->current);
+ }
+ free(tls);
+}
+
+static void
+init_ar_tls(void *ptr)
+{
+ int ret;
+ HEIMDAL_key_create(&ar_key, ar_tls_delete, ret);
+ if (ret == 0)
+ ar_created = 1;
+}
+
+static struct ar_tls *
+autorel_tls(void)
+{
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
+ struct ar_tls *arp;
+ int ret;
+
+ heim_base_once_f(&once, NULL, init_ar_tls);
+ if (!ar_created)
+ return NULL;
+
+ arp = HEIMDAL_getspecific(ar_key);
+ if (arp == NULL) {
+
+ arp = calloc(1, sizeof(*arp));
+ if (arp == NULL)
+ return NULL;
+ HEIMDAL_setspecific(ar_key, arp, ret);
+ if (ret) {
+ free(arp);
+ return NULL;
+ }
+ }
+ return arp;
+
+}
+
+static void
+autorel_dealloc(void *ptr)
+{
+ heim_auto_release_t ar = ptr;
+ struct ar_tls *tls;
+
+ tls = autorel_tls();
+ if (tls == NULL)
+ heim_abort("autorelease pool released on thread w/o autorelease inited");
+
+ heim_auto_release_drain(ar);
+
+ if (!HEIM_TAILQ_EMPTY(&ar->pool))
+ heim_abort("pool not empty after draining");
+
+ HEIMDAL_MUTEX_lock(&tls->tls_mutex);
+ if (tls->current != ptr)
+ heim_abort("autorelease not releaseing top pool");
+
+ tls->current = ar->parent;
+ HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
+}
+
+static int
+autorel_cmp(void *a, void *b)
+{
+ return (a == b);
+}
+
+static unsigned long
+autorel_hash(void *ptr)
+{
+ return (unsigned long)ptr;
+}
+
+
+static struct heim_type_data _heim_autorel_object = {
+ HEIM_TID_AUTORELEASE,
+ "autorelease-pool",
+ NULL,
+ autorel_dealloc,
+ NULL,
+ autorel_cmp,
+ autorel_hash,
+ NULL
+};
+
+/**
+ * Create thread-specific object auto-release pool
+ *
+ * Objects placed on the per-thread auto-release pool (with
+ * heim_auto_release()) can be released in one fell swoop by calling
+ * heim_auto_release_drain().
+ */
+
+heim_auto_release_t
+heim_auto_release_create(void)
+{
+ struct ar_tls *tls = autorel_tls();
+ heim_auto_release_t ar;
+
+ if (tls == NULL)
+ heim_abort("Failed to create/get autorelease head");
+
+ ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release));
+ if (ar) {
+ HEIMDAL_MUTEX_lock(&tls->tls_mutex);
+ if (tls->head == NULL)
+ tls->head = ar;
+ ar->parent = tls->current;
+ tls->current = ar;
+ HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
+ }
+
+ return ar;
+}
+
+/**
+ * Place the current object on the thread's auto-release pool
+ *
+ * @param ptr object
+ */
+
+heim_object_t
+heim_auto_release(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ struct ar_tls *tls = autorel_tls();
+ heim_auto_release_t ar;
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return ptr;
+
+ /* drop from old pool */
+ if ((ar = p->autorelpool) != NULL) {
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
+ p->autorelpool = NULL;
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+ }
+
+ if (tls == NULL || (ar = tls->current) == NULL)
+ heim_abort("no auto relase pool in place, would leak");
+
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel);
+ p->autorelpool = ar;
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+
+ return ptr;
+}
+
+/**
+ * Release all objects on the given auto-release pool
+ */
+
+void
+heim_auto_release_drain(heim_auto_release_t autorel)
+{
+ heim_object_t obj;
+
+ /* release all elements on the tail queue */
+
+ HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
+ while(!HEIM_TAILQ_EMPTY(&autorel->pool)) {
+ obj = HEIM_TAILQ_FIRST(&autorel->pool);
+ HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
+ heim_release(BASE2PTR(obj));
+ HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
+ }
+ HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
+}
+
+/*
+ * Helper for heim_path_vget() and heim_path_delete(). On success
+ * outputs the node named by the path and the parent node and key
+ * (useful for heim_path_delete()).
+ */
+
+static heim_object_t
+heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key,
+ heim_error_t *error, va_list ap)
+{
+ heim_object_t path_element;
+ heim_object_t node, next_node;
+ heim_tid_t node_type;
+
+ *parent = NULL;
+ *key = NULL;
+ if (ptr == NULL)
+ return NULL;
+
+ for (node = ptr; node != NULL; ) {
+ path_element = va_arg(ap, heim_object_t);
+ if (path_element == NULL) {
+ *parent = node;
+ *key = path_element;
+ return node;
+ }
+
+ node_type = heim_get_tid(node);
+ switch (node_type) {
+ case HEIM_TID_ARRAY:
+ case HEIM_TID_DICT:
+ case HEIM_TID_DB:
+ break;
+ default:
+ if (node == ptr)
+ heim_abort("heim_path_get() only operates on container types");
+ return NULL;
+ }
+
+ if (node_type == HEIM_TID_DICT) {
+ next_node = heim_dict_get_value(node, path_element);
+ } else if (node_type == HEIM_TID_DB) {
+ next_node = _heim_db_get_value(node, NULL, path_element, NULL);
+ } else if (node_type == HEIM_TID_ARRAY) {
+ int idx = -1;
+
+ if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
+ idx = heim_number_get_int(path_element);
+ if (idx < 0) {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path_get() path elements "
+ "for array nodes must be "
+ "numeric and positive");
+ return NULL;
+ }
+ next_node = heim_array_get_value(node, idx);
+ } else {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path_get() node in path "
+ "not a container type");
+ return NULL;
+ }
+ node = next_node;
+ }
+ return NULL;
+}
+
+/**
+ * Get a node in a heim_object tree by path
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ap NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return object (not retained) if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_vget(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t p, k;
+
+ return heim_path_vget2(ptr, &p, &k, error, ap);
+}
+
+/**
+ * Get a node in a tree by path, with retained reference
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ap NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return retained object if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_vcopy(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t p, k;
+
+ return heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
+}
+
+/**
+ * Get a node in a tree by path
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ... NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return object (not retained) if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_get(heim_object_t ptr, heim_error_t *error, ...)
+{
+ heim_object_t o;
+ heim_object_t p, k;
+ va_list ap;
+
+ if (ptr == NULL)
+ return NULL;
+
+ va_start(ap, error);
+ o = heim_path_vget2(ptr, &p, &k, error, ap);
+ va_end(ap);
+ return o;
+}
+
+/**
+ * Get a node in a tree by path, with retained reference
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ... NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return retained object if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_copy(heim_object_t ptr, heim_error_t *error, ...)
+{
+ heim_object_t o;
+ heim_object_t p, k;
+ va_list ap;
+
+ if (ptr == NULL)
+ return NULL;
+
+ va_start(ap, error);
+ o = heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
+ va_end(ap);
+ return o;
+}
+
+/**
+ * Create a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param size the size of the heim_dict_t nodes to be created
+ * @param leaf leaf node to be added, if any
+ * @param error error (output)
+ * @param ap NULL-terminated of path component objects
+ *
+ * Create a path of heim_dict_t interior nodes in a given heim_object_t
+ * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
+ * then the leaf is not deleted).
+ *
+ * @return 0 on success, else a system error
+ *
+ * @addtogroup heimbase
+ */
+
+int
+heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, va_list ap)
+{
+ heim_object_t path_element = va_arg(ap, heim_object_t);
+ heim_object_t next_path_element = NULL;
+ heim_object_t node = ptr;
+ heim_object_t next_node = NULL;
+ heim_tid_t node_type;
+ int ret = 0;
+
+ if (ptr == NULL)
+ heim_abort("heim_path_vcreate() does not create root nodes");
+
+ while (path_element != NULL) {
+ next_path_element = va_arg(ap, heim_object_t);
+ node_type = heim_get_tid(node);
+
+ if (node_type == HEIM_TID_DICT) {
+ next_node = heim_dict_get_value(node, path_element);
+ } else if (node_type == HEIM_TID_ARRAY) {
+ int idx = -1;
+
+ if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
+ idx = heim_number_get_int(path_element);
+ if (idx < 0) {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path() path elements for "
+ "array nodes must be numeric "
+ "and positive");
+ return EINVAL;
+ }
+ if (idx < heim_array_get_length(node))
+ next_node = heim_array_get_value(node, idx);
+ else
+ next_node = NULL;
+ } else if (node_type == HEIM_TID_DB && next_path_element != NULL) {
+ if (error)
+ *error = heim_error_create(EINVAL, "Interior node is a DB");
+ return EINVAL;
+ }
+
+ if (next_path_element == NULL)
+ break;
+
+ /* Create missing interior node */
+ if (next_node == NULL) {
+ next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */
+ if (next_node == NULL) {
+ ret = ENOMEM;
+ goto err;
+ }
+
+ if (node_type == HEIM_TID_DICT) {
+ ret = heim_dict_set_value(node, path_element, next_node);
+ } else if (node_type == HEIM_TID_ARRAY &&
+ heim_number_get_int(path_element) <= heim_array_get_length(node)) {
+ ret = heim_array_insert_value(node,
+ heim_number_get_int(path_element),
+ next_node);
+ } else {
+ ret = EINVAL;
+ if (error)
+ *error = heim_error_create(ret, "Node in path not a "
+ "container");
+ }
+ heim_release(next_node);
+ if (ret)
+ goto err;
+ }
+
+ path_element = next_path_element;
+ node = next_node;
+ next_node = NULL;
+ }
+
+ if (path_element == NULL)
+ goto err;
+
+ /* Add the leaf */
+ if (leaf != NULL) {
+ if (node_type == HEIM_TID_DICT)
+ ret = heim_dict_set_value(node, path_element, leaf);
+ else
+ ret = heim_array_insert_value(node,
+ heim_number_get_int(path_element),
+ leaf);
+ }
+ return ret;
+
+err:
+ if (error && !*error) {
+ if (ret == ENOMEM)
+ *error = heim_error_create_enomem();
+ else
+ *error = heim_error_create(ret, "Could not set "
+ "dict value");
+ }
+ return ret;
+}
+
+/**
+ * Create a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param size the size of the heim_dict_t nodes to be created
+ * @param leaf leaf node to be added, if any
+ * @param error error (output)
+ * @param ... NULL-terminated list of path component objects
+ *
+ * Create a path of heim_dict_t interior nodes in a given heim_object_t
+ * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
+ * then the leaf is not deleted).
+ *
+ * @return 0 on success, else a system error
+ *
+ * @addtogroup heimbase
+ */
+
+int
+heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, error);
+ ret = heim_path_vcreate(ptr, size, leaf, error, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Delete leaf node named by a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param error error (output)
+ * @param ap NULL-terminated list of path component objects
+ *
+ * @addtogroup heimbase
+ */
+
+void
+heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t parent, key, child;
+
+ child = heim_path_vget2(ptr, &parent, &key, error, ap);
+ if (child != NULL) {
+ if (heim_get_tid(parent) == HEIM_TID_DICT)
+ heim_dict_delete_key(parent, key);
+ else if (heim_get_tid(parent) == HEIM_TID_DB)
+ heim_db_delete_key(parent, NULL, key, error);
+ else if (heim_get_tid(parent) == HEIM_TID_ARRAY)
+ heim_array_delete_value(parent, heim_number_get_int(key));
+ heim_release(child);
+ }
+}
+
+/**
+ * Delete leaf node named by a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param error error (output)
+ * @param ap NULL-terminated list of path component objects
+ *
+ * @addtogroup heimbase
+ */
+
+void
+heim_path_delete(heim_object_t ptr, heim_error_t *error, ...)
+{
+ va_list ap;
+
+ va_start(ap, error);
+ heim_path_vdelete(ptr, error, ap);
+ va_end(ap);
+ return;
+}
+
diff --git a/lib/base/heimbase.h b/lib/base/heimbase.h
new file mode 100644
index 000000000000..157cbd4c1105
--- /dev/null
+++ b/lib/base/heimbase.h
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
+ */
+
+#ifndef HEIM_BASE_H
+#define HEIM_BASE_H 1
+
+#include <sys/types.h>
+#if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
+#include <pthread.h>
+#endif
+#include <krb5-types.h>
+#include <stdarg.h>
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+#ifndef false
+#define false 0
+#endif
+#ifndef true
+#define true 1
+#endif
+#endif
+
+#define HEIM_BASE_API_VERSION 20130210
+
+typedef void * heim_object_t;
+typedef unsigned int heim_tid_t;
+typedef heim_object_t heim_bool_t;
+typedef heim_object_t heim_null_t;
+#ifdef WIN32
+typedef LONG heim_base_once_t;
+#define HEIM_BASE_ONCE_INIT 0
+#elif defined(HAVE_DISPATCH_DISPATCH_H)
+typedef long heim_base_once_t; /* XXX arch dependant */
+#define HEIM_BASE_ONCE_INIT 0
+#elif defined(ENABLE_PTHREAD_SUPPORT)
+typedef pthread_once_t heim_base_once_t;
+#define HEIM_BASE_ONCE_INIT PTHREAD_ONCE_INIT
+#else
+typedef long heim_base_once_t; /* XXX arch dependant */
+#define HEIM_BASE_ONCE_INIT 0
+#endif
+
+#if !defined(__has_extension)
+#define __has_extension(x) 0
+#endif
+
+#define HEIM_REQUIRE_GNUC(m,n,p) \
+ (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) >= \
+ (((m) * 10000) + ((n) * 100) + (p)))
+
+
+#if __has_extension(__builtin_expect) || HEIM_REQUIRE_GNUC(3,0,0)
+#define heim_builtin_expect(_op,_res) __builtin_expect(_op,_res)
+#else
+#define heim_builtin_expect(_op,_res) (_op)
+#endif
+
+
+void * heim_retain(heim_object_t);
+void heim_release(heim_object_t);
+
+void heim_show(heim_object_t);
+
+typedef void (*heim_type_dealloc)(void *);
+
+void *
+heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc);
+
+heim_tid_t
+heim_get_tid(heim_object_t object);
+
+int
+heim_cmp(heim_object_t a, heim_object_t b);
+
+unsigned long
+heim_get_hash(heim_object_t ptr);
+
+void
+heim_base_once_f(heim_base_once_t *, void *, void (*)(void *));
+
+void
+heim_abort(const char *fmt, ...)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2));
+
+void
+heim_abortv(const char *fmt, va_list ap)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0));
+
+#define heim_assert(e,t) \
+ (heim_builtin_expect(!(e), 0) ? heim_abort(t ":" #e) : (void)0)
+
+/*
+ *
+ */
+
+heim_null_t
+heim_null_create(void);
+
+heim_bool_t
+heim_bool_create(int);
+
+int
+heim_bool_val(heim_bool_t);
+
+/*
+ * Array
+ */
+
+typedef struct heim_array_data *heim_array_t;
+
+heim_array_t heim_array_create(void);
+heim_tid_t heim_array_get_type_id(void);
+
+typedef void (*heim_array_iterator_f_t)(heim_object_t, void *, int *);
+typedef int (*heim_array_filter_f_t)(heim_object_t, void *);
+
+int heim_array_append_value(heim_array_t, heim_object_t);
+int heim_array_insert_value(heim_array_t, size_t idx, heim_object_t);
+void heim_array_iterate_f(heim_array_t, void *, heim_array_iterator_f_t);
+void heim_array_iterate_reverse_f(heim_array_t, void *, heim_array_iterator_f_t);
+#ifdef __BLOCKS__
+void heim_array_iterate(heim_array_t, void (^)(heim_object_t, int *));
+void heim_array_iterate_reverse(heim_array_t, void (^)(heim_object_t, int *));
+#endif
+size_t heim_array_get_length(heim_array_t);
+heim_object_t
+ heim_array_get_value(heim_array_t, size_t);
+heim_object_t
+ heim_array_copy_value(heim_array_t, size_t);
+void heim_array_set_value(heim_array_t, size_t, heim_object_t);
+void heim_array_delete_value(heim_array_t, size_t);
+void heim_array_filter_f(heim_array_t, void *, heim_array_filter_f_t);
+#ifdef __BLOCKS__
+void heim_array_filter(heim_array_t, int (^)(heim_object_t));
+#endif
+
+/*
+ * Dict
+ */
+
+typedef struct heim_dict_data *heim_dict_t;
+
+heim_dict_t heim_dict_create(size_t size);
+heim_tid_t heim_dict_get_type_id(void);
+
+typedef void (*heim_dict_iterator_f_t)(heim_object_t, heim_object_t, void *);
+
+int heim_dict_set_value(heim_dict_t, heim_object_t, heim_object_t);
+void heim_dict_iterate_f(heim_dict_t, void *, heim_dict_iterator_f_t);
+#ifdef __BLOCKS__
+void heim_dict_iterate(heim_dict_t, void (^)(heim_object_t, heim_object_t));
+#endif
+
+heim_object_t
+ heim_dict_get_value(heim_dict_t, heim_object_t);
+heim_object_t
+ heim_dict_copy_value(heim_dict_t, heim_object_t);
+void heim_dict_delete_key(heim_dict_t, heim_object_t);
+
+/*
+ * String
+ */
+
+typedef struct heim_string_data *heim_string_t;
+typedef void (*heim_string_free_f_t)(void *);
+
+heim_string_t heim_string_create(const char *);
+heim_string_t heim_string_ref_create(const char *, heim_string_free_f_t);
+heim_string_t heim_string_create_with_bytes(const void *, size_t);
+heim_string_t heim_string_ref_create_with_bytes(const void *, size_t,
+ heim_string_free_f_t);
+heim_string_t heim_string_create_with_format(const char *, ...);
+heim_tid_t heim_string_get_type_id(void);
+const char * heim_string_get_utf8(heim_string_t);
+
+#define HSTR(_str) (__heim_string_constant("" _str ""))
+heim_string_t __heim_string_constant(const char *);
+
+/*
+ * Errors
+ */
+
+typedef struct heim_error * heim_error_t;
+
+heim_error_t heim_error_create_enomem(void);
+
+heim_error_t heim_error_create(int, const char *, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3));
+
+void heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4));
+
+heim_error_t heim_error_createv(int, const char *, va_list)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0));
+
+heim_string_t heim_error_copy_string(heim_error_t);
+int heim_error_get_code(heim_error_t);
+
+heim_error_t heim_error_append(heim_error_t, heim_error_t);
+
+/*
+ * Path
+ */
+
+heim_object_t heim_path_get(heim_object_t ptr, heim_error_t *error, ...);
+heim_object_t heim_path_copy(heim_object_t ptr, heim_error_t *error, ...);
+heim_object_t heim_path_vget(heim_object_t ptr, heim_error_t *error,
+ va_list ap);
+heim_object_t heim_path_vcopy(heim_object_t ptr, heim_error_t *error,
+ va_list ap);
+
+int heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, va_list ap);
+int heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, ...);
+
+void heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap);
+void heim_path_delete(heim_object_t ptr, heim_error_t *error, ...);
+
+/*
+ * Data (octet strings)
+ */
+
+#ifndef __HEIM_BASE_DATA__
+#define __HEIM_BASE_DATA__
+struct heim_base_data {
+ size_t length;
+ void *data;
+};
+typedef struct heim_base_data heim_octet_string;
+#endif
+
+typedef struct heim_base_data * heim_data_t;
+typedef void (*heim_data_free_f_t)(void *);
+
+heim_data_t heim_data_create(const void *, size_t);
+heim_data_t heim_data_ref_create(const void *, size_t, heim_data_free_f_t);
+heim_tid_t heim_data_get_type_id(void);
+const heim_octet_string *
+ heim_data_get_data(heim_data_t);
+const void * heim_data_get_ptr(heim_data_t);
+size_t heim_data_get_length(heim_data_t);
+
+/*
+ * DB
+ */
+
+typedef struct heim_db_data *heim_db_t;
+
+typedef void (*heim_db_iterator_f_t)(heim_data_t, heim_data_t, void *);
+
+typedef int (*heim_db_plug_open_f_t)(void *, const char *, const char *,
+ heim_dict_t, void **, heim_error_t *);
+typedef int (*heim_db_plug_clone_f_t)(void *, void **, heim_error_t *);
+typedef int (*heim_db_plug_close_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_lock_f_t)(void *, int, heim_error_t *);
+typedef int (*heim_db_plug_unlock_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_sync_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_begin_f_t)(void *, int, heim_error_t *);
+typedef int (*heim_db_plug_commit_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_rollback_f_t)(void *, heim_error_t *);
+typedef heim_data_t (*heim_db_plug_copy_value_f_t)(void *, heim_string_t,
+ heim_data_t,
+ heim_error_t *);
+typedef int (*heim_db_plug_set_value_f_t)(void *, heim_string_t, heim_data_t,
+ heim_data_t, heim_error_t *);
+typedef int (*heim_db_plug_del_key_f_t)(void *, heim_string_t, heim_data_t,
+ heim_error_t *);
+typedef void (*heim_db_plug_iter_f_t)(void *, heim_string_t, void *,
+ heim_db_iterator_f_t, heim_error_t *);
+
+struct heim_db_type {
+ int version;
+ heim_db_plug_open_f_t openf;
+ heim_db_plug_clone_f_t clonef;
+ heim_db_plug_close_f_t closef;
+ heim_db_plug_lock_f_t lockf;
+ heim_db_plug_unlock_f_t unlockf;
+ heim_db_plug_sync_f_t syncf;
+ heim_db_plug_begin_f_t beginf;
+ heim_db_plug_commit_f_t commitf;
+ heim_db_plug_rollback_f_t rollbackf;
+ heim_db_plug_copy_value_f_t copyf;
+ heim_db_plug_set_value_f_t setf;
+ heim_db_plug_del_key_f_t delf;
+ heim_db_plug_iter_f_t iterf;
+};
+
+extern struct heim_db_type heim_sorted_text_file_dbtype;
+
+#define HEIM_DB_TYPE_VERSION_01 1
+
+int heim_db_register(const char *dbtype,
+ void *data,
+ struct heim_db_type *plugin);
+
+heim_db_t heim_db_create(const char *dbtype, const char *dbname,
+ heim_dict_t options, heim_error_t *error);
+heim_db_t heim_db_clone(heim_db_t, heim_error_t *);
+int heim_db_begin(heim_db_t, int, heim_error_t *);
+int heim_db_commit(heim_db_t, heim_error_t *);
+int heim_db_rollback(heim_db_t, heim_error_t *);
+heim_tid_t heim_db_get_type_id(void);
+
+int heim_db_set_value(heim_db_t, heim_string_t, heim_data_t, heim_data_t,
+ heim_error_t *);
+heim_data_t heim_db_copy_value(heim_db_t, heim_string_t, heim_data_t,
+ heim_error_t *);
+int heim_db_delete_key(heim_db_t, heim_string_t, heim_data_t,
+ heim_error_t *);
+void heim_db_iterate_f(heim_db_t, heim_string_t, void *,
+ heim_db_iterator_f_t, heim_error_t *);
+#ifdef __BLOCKS__
+void heim_db_iterate(heim_db_t, heim_string_t,
+ void (^)(heim_data_t, heim_data_t), heim_error_t *);
+#endif
+
+
+/*
+ * Number
+ */
+
+typedef struct heim_number_data *heim_number_t;
+
+heim_number_t heim_number_create(int);
+heim_tid_t heim_number_get_type_id(void);
+int heim_number_get_int(heim_number_t);
+
+/*
+ *
+ */
+
+typedef struct heim_auto_release * heim_auto_release_t;
+
+heim_auto_release_t heim_auto_release_create(void);
+void heim_auto_release_drain(heim_auto_release_t);
+heim_object_t heim_auto_release(heim_object_t);
+
+/*
+ * JSON
+ */
+typedef enum heim_json_flags {
+ HEIM_JSON_F_NO_C_NULL = 1,
+ HEIM_JSON_F_STRICT_STRINGS = 2,
+ HEIM_JSON_F_NO_DATA = 4,
+ HEIM_JSON_F_NO_DATA_DICT = 8,
+ HEIM_JSON_F_STRICT_DICT = 16,
+ HEIM_JSON_F_STRICT = 31,
+ HEIM_JSON_F_CNULL2JSNULL = 32,
+ HEIM_JSON_F_TRY_DECODE_DATA = 64,
+ HEIM_JSON_F_ONE_LINE = 128
+} heim_json_flags_t;
+
+heim_object_t heim_json_create(const char *, size_t, heim_json_flags_t,
+ heim_error_t *);
+heim_object_t heim_json_create_with_bytes(const void *, size_t, size_t,
+ heim_json_flags_t,
+ heim_error_t *);
+heim_string_t heim_json_copy_serialize(heim_object_t, heim_json_flags_t,
+ heim_error_t *);
+
+
+/*
+ * Debug
+ */
+
+heim_string_t
+heim_description(heim_object_t ptr);
+
+/*
+ * Binary search.
+ *
+ * Note: these are private until integrated into the heimbase object system.
+ */
+typedef struct bsearch_file_handle *bsearch_file_handle;
+int _bsearch_text(const char *buf, size_t buf_sz, const char *key,
+ char **value, size_t *location, size_t *loops);
+int _bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
+ bsearch_file_handle *bfh, size_t *reads);
+int _bsearch_file(bsearch_file_handle bfh, const char *key, char **value,
+ size_t *location, size_t *loops, size_t *reads);
+void _bsearch_file_info(bsearch_file_handle bfh, size_t *page_sz,
+ size_t *max_sz, int *blockwise);
+void _bsearch_file_close(bsearch_file_handle *bfh);
+
+/*
+ * Thread-specific keys
+ */
+
+int heim_w32_key_create(unsigned long *, void (*)(void *));
+int heim_w32_delete_key(unsigned long);
+int heim_w32_setspecific(unsigned long, void *);
+void *heim_w32_getspecific(unsigned long);
+void heim_w32_service_thread_detach(void *);
+
+#endif /* HEIM_BASE_H */
diff --git a/lib/base/heimbasepriv.h b/lib/base/heimbasepriv.h
new file mode 100644
index 000000000000..eb155006fe0d
--- /dev/null
+++ b/lib/base/heimbasepriv.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
+ */
+
+#if defined(HEIM_BASE_MAINTAINER) && defined(ENABLE_PTHREAD_SUPPORT)
+#define HEIM_WIN32_TLS
+#elif defined(WIN32)
+#define HEIM_WIN32_TLS
+#endif
+
+typedef void (*heim_type_init)(void *);
+typedef heim_object_t (*heim_type_copy)(void *);
+typedef int (*heim_type_cmp)(void *, void *);
+typedef unsigned long (*heim_type_hash)(void *);
+typedef heim_string_t (*heim_type_description)(void *);
+
+typedef struct heim_type_data *heim_type_t;
+
+enum {
+ HEIM_TID_NUMBER = 0,
+ HEIM_TID_NULL = 1,
+ HEIM_TID_BOOL = 2,
+ HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */
+ HEIM_TID_MEMORY = 128,
+ HEIM_TID_ARRAY = 129,
+ HEIM_TID_DICT = 130,
+ HEIM_TID_STRING = 131,
+ HEIM_TID_AUTORELEASE = 132,
+ HEIM_TID_ERROR = 133,
+ HEIM_TID_DATA = 134,
+ HEIM_TID_DB = 135,
+ HEIM_TID_USER = 255
+
+};
+
+struct heim_type_data {
+ heim_tid_t tid;
+ const char *name;
+ heim_type_init init;
+ heim_type_dealloc dealloc;
+ heim_type_copy copy;
+ heim_type_cmp cmp;
+ heim_type_hash hash;
+ heim_type_description desc;
+};
+
+heim_type_t _heim_get_isa(heim_object_t);
+
+heim_type_t
+_heim_create_type(const char *name,
+ heim_type_init init,
+ heim_type_dealloc dealloc,
+ heim_type_copy copy,
+ heim_type_cmp cmp,
+ heim_type_hash hash,
+ heim_type_description desc);
+
+heim_object_t
+_heim_alloc_object(heim_type_t type, size_t size);
+
+void *
+_heim_get_isaextra(heim_object_t o, size_t idx);
+
+heim_tid_t
+_heim_type_get_tid(heim_type_t type);
+
+void
+_heim_make_permanent(heim_object_t ptr);
+
+heim_data_t
+_heim_db_get_value(heim_db_t, heim_string_t, heim_data_t, heim_error_t *);
+
+
+/* tagged tid */
+extern struct heim_type_data _heim_null_object;
+extern struct heim_type_data _heim_bool_object;
+extern struct heim_type_data _heim_number_object;
+extern struct heim_type_data _heim_string_object;
diff --git a/lib/base/heimqueue.h b/lib/base/heimqueue.h
new file mode 100644
index 000000000000..423a68478792
--- /dev/null
+++ b/lib/base/heimqueue.h
@@ -0,0 +1,167 @@
+/* $NetBSD: queue.h,v 1.38 2004/04/18 14:12:05 lukem Exp $ */
+/* $Id$ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _HEIM_QUEUE_H_
+#define _HEIM_QUEUE_H_
+
+/*
+ * Tail queue definitions.
+ */
+#define HEIM_TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define HEIM_TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+#define HEIM_TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if defined(_KERNEL) && defined(QUEUEDEBUG)
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ panic("HEIM_TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ panic("HEIM_TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ panic("HEIM_TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("HEIM_TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ panic("HEIM_TAILQ_PREREMOVE head %p elm %p %s:%d", \
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define HEIM_TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_HEIM_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var); \
+ (var) = ((var)->field.tqe_next))
+
+#define HEIM_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
+ (var); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+/*
+ * Tail queue access methods.
+ */
+#define HEIM_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define HEIM_TAILQ_FIRST(head) ((head)->tqh_first)
+#define HEIM_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define HEIM_TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define HEIM_TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+#endif /* !_HEIM_QUEUE_H_ */
diff --git a/lib/base/json.c b/lib/base/json.c
new file mode 100644
index 000000000000..2ef371b975ea
--- /dev/null
+++ b/lib/base/json.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+#include <ctype.h>
+#include <base64.h>
+
+static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT;
+static heim_string_t heim_tid_data_uuid_key = NULL;
+static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void
+json_init_once(void *arg)
+{
+ heim_tid_data_uuid_key = __heim_string_constant("heimdal-type-data-76d7fca2-d0da-4b20-a126-1a10f8a0eae6");
+}
+
+struct twojson {
+ void *ctx;
+ void (*out)(void *, const char *);
+ size_t indent;
+ heim_json_flags_t flags;
+ int ret;
+ int first;
+};
+
+struct heim_strbuf {
+ char *str;
+ size_t len;
+ size_t alloced;
+ int enomem;
+ heim_json_flags_t flags;
+};
+
+static int
+base2json(heim_object_t, struct twojson *);
+
+static void
+indent(struct twojson *j)
+{
+ size_t i = j->indent;
+ if (j->flags & HEIM_JSON_F_ONE_LINE)
+ return;
+ while (i--)
+ j->out(j->ctx, "\t");
+}
+
+static void
+array2json(heim_object_t value, void *ctx, int *stop)
+{
+ struct twojson *j = ctx;
+ if (j->ret)
+ return;
+ if (j->first) {
+ j->first = 0;
+ } else {
+ j->out(j->ctx, NULL); /* eat previous '\n' if possible */
+ j->out(j->ctx, ",\n");
+ }
+ j->ret = base2json(value, j);
+}
+
+static void
+dict2json(heim_object_t key, heim_object_t value, void *ctx)
+{
+ struct twojson *j = ctx;
+ if (j->ret)
+ return;
+ if (j->first) {
+ j->first = 0;
+ } else {
+ j->out(j->ctx, NULL); /* eat previous '\n' if possible */
+ j->out(j->ctx, ",\n");
+ }
+ j->ret = base2json(key, j);
+ if (j->ret)
+ return;
+ j->out(j->ctx, " : \n");
+ j->indent++;
+ j->ret = base2json(value, j);
+ if (j->ret)
+ return;
+ j->indent--;
+}
+
+static int
+base2json(heim_object_t obj, struct twojson *j)
+{
+ heim_tid_t type;
+ int first = 0;
+
+ if (obj == NULL) {
+ if (j->flags & HEIM_JSON_F_CNULL2JSNULL) {
+ obj = heim_null_create();
+ } else if (j->flags & HEIM_JSON_F_NO_C_NULL) {
+ return EINVAL;
+ } else {
+ indent(j);
+ j->out(j->ctx, "<NULL>\n"); /* This is NOT valid JSON! */
+ return 0;
+ }
+ }
+
+ type = heim_get_tid(obj);
+ switch (type) {
+ case HEIM_TID_ARRAY:
+ indent(j);
+ j->out(j->ctx, "[\n");
+ j->indent++;
+ first = j->first;
+ j->first = 1;
+ heim_array_iterate_f(obj, j, array2json);
+ j->indent--;
+ if (!j->first)
+ j->out(j->ctx, "\n");
+ indent(j);
+ j->out(j->ctx, "]\n");
+ j->first = first;
+ break;
+
+ case HEIM_TID_DICT:
+ indent(j);
+ j->out(j->ctx, "{\n");
+ j->indent++;
+ first = j->first;
+ j->first = 1;
+ heim_dict_iterate_f(obj, j, dict2json);
+ j->indent--;
+ if (!j->first)
+ j->out(j->ctx, "\n");
+ indent(j);
+ j->out(j->ctx, "}\n");
+ j->first = first;
+ break;
+
+ case HEIM_TID_STRING:
+ indent(j);
+ j->out(j->ctx, "\"");
+ j->out(j->ctx, heim_string_get_utf8(obj));
+ j->out(j->ctx, "\"");
+ break;
+
+ case HEIM_TID_DATA: {
+ heim_dict_t d;
+ heim_string_t v;
+ const heim_octet_string *data;
+ char *b64 = NULL;
+ int ret;
+
+ if (j->flags & HEIM_JSON_F_NO_DATA)
+ return EINVAL; /* JSON doesn't do binary */
+
+ data = heim_data_get_data(obj);
+ ret = rk_base64_encode(data->data, data->length, &b64);
+ if (ret < 0 || b64 == NULL)
+ return ENOMEM;
+
+ if (j->flags & HEIM_JSON_F_NO_DATA_DICT) {
+ indent(j);
+ j->out(j->ctx, "\"");
+ j->out(j->ctx, b64); /* base64-encode; hope there's no aliasing */
+ j->out(j->ctx, "\"");
+ free(b64);
+ } else {
+ /*
+ * JSON has no way to represent binary data, therefore the
+ * following is a Heimdal-specific convention.
+ *
+ * We encode binary data as a dict with a single very magic
+ * key with a base64-encoded value. The magic key includes
+ * a uuid, so we're not likely to alias accidentally.
+ */
+ d = heim_dict_create(2);
+ if (d == NULL) {
+ free(b64);
+ return ENOMEM;
+ }
+ v = heim_string_ref_create(b64, free);
+ if (v == NULL) {
+ free(b64);
+ heim_release(d);
+ return ENOMEM;
+ }
+ ret = heim_dict_set_value(d, heim_tid_data_uuid_key, v);
+ heim_release(v);
+ if (ret) {
+ heim_release(d);
+ return ENOMEM;
+ }
+ ret = base2json(d, j);
+ heim_release(d);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+
+ case HEIM_TID_NUMBER: {
+ char num[32];
+ indent(j);
+ snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
+ j->out(j->ctx, num);
+ break;
+ }
+ case HEIM_TID_NULL:
+ indent(j);
+ j->out(j->ctx, "null");
+ break;
+ case HEIM_TID_BOOL:
+ indent(j);
+ j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
+ void (*out)(void *, const char *))
+{
+ struct twojson j;
+
+ if (flags & HEIM_JSON_F_STRICT_STRINGS)
+ return ENOTSUP; /* Sorry, not yet! */
+
+ heim_base_once_f(&heim_json_once, NULL, json_init_once);
+
+ j.indent = 0;
+ j.ctx = ctx;
+ j.out = out;
+ j.flags = flags;
+ j.ret = 0;
+ j.first = 1;
+
+ return base2json(obj, &j);
+}
+
+
+/*
+ *
+ */
+
+struct parse_ctx {
+ unsigned long lineno;
+ const uint8_t *p;
+ const uint8_t *pstart;
+ const uint8_t *pend;
+ heim_error_t error;
+ size_t depth;
+ heim_json_flags_t flags;
+};
+
+
+static heim_object_t
+parse_value(struct parse_ctx *ctx);
+
+/*
+ * This function eats whitespace, but, critically, it also succeeds
+ * only if there's anything left to parse.
+ */
+static int
+white_spaces(struct parse_ctx *ctx)
+{
+ while (ctx->p < ctx->pend) {
+ uint8_t c = *ctx->p;
+ if (c == ' ' || c == '\t' || c == '\r') {
+
+ } else if (c == '\n') {
+ ctx->lineno++;
+ } else
+ return 0;
+ (ctx->p)++;
+ }
+ return -1;
+}
+
+static int
+is_number(uint8_t n)
+{
+ return ('0' <= n && n <= '9');
+}
+
+static heim_number_t
+parse_number(struct parse_ctx *ctx)
+{
+ int number = 0, neg = 1;
+
+ if (ctx->p >= ctx->pend)
+ return NULL;
+
+ if (*ctx->p == '-') {
+ if (ctx->p + 1 >= ctx->pend)
+ return NULL;
+ neg = -1;
+ ctx->p += 1;
+ }
+
+ while (ctx->p < ctx->pend) {
+ if (is_number(*ctx->p)) {
+ number = (number * 10) + (*ctx->p - '0');
+ } else {
+ break;
+ }
+ ctx->p += 1;
+ }
+
+ return heim_number_create(number * neg);
+}
+
+static heim_string_t
+parse_string(struct parse_ctx *ctx)
+{
+ const uint8_t *start;
+ int quote = 0;
+
+ if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) {
+ ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding "
+ "not yet supported");
+ return NULL;
+ }
+
+ if (*ctx->p != '"') {
+ ctx->error = heim_error_create(EINVAL, "Expected a JSON string but "
+ "found something else at line %lu",
+ ctx->lineno);
+ return NULL;
+ }
+ start = ++ctx->p;
+
+ while (ctx->p < ctx->pend) {
+ if (*ctx->p == '\n') {
+ ctx->lineno++;
+ } else if (*ctx->p == '\\') {
+ if (ctx->p + 1 == ctx->pend)
+ goto out;
+ ctx->p++;
+ quote = 1;
+ } else if (*ctx->p == '"') {
+ heim_object_t o;
+
+ if (quote) {
+ char *p0, *p;
+ p = p0 = malloc(ctx->p - start);
+ if (p == NULL)
+ goto out;
+ while (start < ctx->p) {
+ if (*start == '\\') {
+ start++;
+ /* XXX validate quoted char */
+ }
+ *p++ = *start++;
+ }
+ o = heim_string_create_with_bytes(p0, p - p0);
+ free(p0);
+ } else {
+ o = heim_string_create_with_bytes(start, ctx->p - start);
+ if (o == NULL) {
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ /* If we can decode as base64, then let's */
+ if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) {
+ void *buf;
+ size_t len;
+ const char *s;
+
+ s = heim_string_get_utf8(o);
+ len = strlen(s);
+
+ if (len >= 4 && strspn(s, base64_chars) >= len - 2) {
+ buf = malloc(len);
+ if (buf == NULL) {
+ heim_release(o);
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+ len = rk_base64_decode(s, buf);
+ if (len == -1) {
+ free(buf);
+ return o;
+ }
+ heim_release(o);
+ o = heim_data_ref_create(buf, len, free);
+ }
+ }
+ }
+ ctx->p += 1;
+
+ return o;
+ }
+ ctx->p += 1;
+ }
+ out:
+ ctx->error = heim_error_create(EINVAL, "ran out of string");
+ return NULL;
+}
+
+static int
+parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
+{
+ heim_string_t key;
+ heim_object_t value;
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == '}') {
+ ctx->p++;
+ return 0;
+ }
+
+ if (ctx->flags & HEIM_JSON_F_STRICT_DICT)
+ /* JSON allows only string keys */
+ key = parse_string(ctx);
+ else
+ /* heim_dict_t allows any heim_object_t as key */
+ key = parse_value(ctx);
+ if (key == NULL)
+ /* Even heim_dict_t does not allow C NULLs as keys though! */
+ return -1;
+
+ if (white_spaces(ctx)) {
+ heim_release(key);
+ return -1;
+ }
+
+ if (*ctx->p != ':') {
+ heim_release(key);
+ return -1;
+ }
+
+ ctx->p += 1; /* safe because we call white_spaces() next */
+
+ if (white_spaces(ctx)) {
+ heim_release(key);
+ return -1;
+ }
+
+ value = parse_value(ctx);
+ if (value == NULL &&
+ (ctx->error != NULL || (ctx->flags & HEIM_JSON_F_NO_C_NULL))) {
+ if (ctx->error == NULL)
+ ctx->error = heim_error_create(EINVAL, "Invalid JSON encoding");
+ heim_release(key);
+ return -1;
+ }
+ heim_dict_set_value(dict, key, value);
+ heim_release(key);
+ heim_release(value);
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == '}') {
+ /*
+ * Return 1 but don't consume the '}' so we can count the one
+ * pair in a one-pair dict
+ */
+ return 1;
+ } else if (*ctx->p == ',') {
+ ctx->p++;
+ return 1;
+ }
+ return -1;
+}
+
+static heim_dict_t
+parse_dict(struct parse_ctx *ctx)
+{
+ heim_dict_t dict;
+ size_t count = 0;
+ int ret;
+
+ heim_assert(*ctx->p == '{', "string doesn't start with {");
+
+ dict = heim_dict_create(11);
+ if (dict == NULL) {
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */
+
+ while ((ret = parse_pair(dict, ctx)) > 0)
+ count++;
+ if (ret < 0) {
+ heim_release(dict);
+ return NULL;
+ }
+ if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) {
+ heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key);
+
+ /*
+ * Binary data encoded as a dict with a single magic key with
+ * base64-encoded value? Decode as heim_data_t.
+ */
+ if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) {
+ void *buf;
+ size_t len;
+
+ buf = malloc(strlen(heim_string_get_utf8(v)));
+ if (buf == NULL) {
+ heim_release(dict);
+ heim_release(v);
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+ len = rk_base64_decode(heim_string_get_utf8(v), buf);
+ heim_release(v);
+ if (len == -1) {
+ free(buf);
+ return dict; /* assume aliasing accident */
+ }
+ heim_release(dict);
+ return (heim_dict_t)heim_data_ref_create(buf, len, free);
+ }
+ }
+ return dict;
+}
+
+static int
+parse_item(heim_array_t array, struct parse_ctx *ctx)
+{
+ heim_object_t value;
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == ']') {
+ ctx->p++; /* safe because parse_value() calls white_spaces() first */
+ return 0;
+ }
+
+ value = parse_value(ctx);
+ if (value == NULL &&
+ (ctx->error || (ctx->flags & HEIM_JSON_F_NO_C_NULL)))
+ return -1;
+
+ heim_array_append_value(array, value);
+ heim_release(value);
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == ']') {
+ ctx->p++;
+ return 0;
+ } else if (*ctx->p == ',') {
+ ctx->p++;
+ return 1;
+ }
+ return -1;
+}
+
+static heim_array_t
+parse_array(struct parse_ctx *ctx)
+{
+ heim_array_t array = heim_array_create();
+ int ret;
+
+ heim_assert(*ctx->p == '[', "array doesn't start with [");
+ ctx->p += 1;
+
+ while ((ret = parse_item(array, ctx)) > 0)
+ ;
+ if (ret < 0) {
+ heim_release(array);
+ return NULL;
+ }
+ return array;
+}
+
+static heim_object_t
+parse_value(struct parse_ctx *ctx)
+{
+ size_t len;
+ heim_object_t o;
+
+ if (white_spaces(ctx))
+ return NULL;
+
+ if (*ctx->p == '"') {
+ return parse_string(ctx);
+ } else if (*ctx->p == '{') {
+ if (ctx->depth-- == 1) {
+ ctx->error = heim_error_create(EINVAL, "JSON object too deep");
+ return NULL;
+ }
+ o = parse_dict(ctx);
+ ctx->depth++;
+ return o;
+ } else if (*ctx->p == '[') {
+ if (ctx->depth-- == 1) {
+ ctx->error = heim_error_create(EINVAL, "JSON object too deep");
+ return NULL;
+ }
+ o = parse_array(ctx);
+ ctx->depth++;
+ return o;
+ } else if (is_number(*ctx->p) || *ctx->p == '-') {
+ return parse_number(ctx);
+ }
+
+ len = ctx->pend - ctx->p;
+
+ if ((ctx->flags & HEIM_JSON_F_NO_C_NULL) == 0 &&
+ len >= 6 && memcmp(ctx->p, "<NULL>", 6) == 0) {
+ ctx->p += 6;
+ return heim_null_create();
+ } else if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
+ ctx->p += 4;
+ return heim_null_create();
+ } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) {
+ ctx->p += 4;
+ return heim_bool_create(1);
+ } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) {
+ ctx->p += 5;
+ return heim_bool_create(0);
+ }
+
+ ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu",
+ (char)*ctx->p,
+ (unsigned long)(ctx->p - ctx->pstart),
+ ctx->lineno);
+ return NULL;
+}
+
+
+heim_object_t
+heim_json_create(const char *string, size_t max_depth, heim_json_flags_t flags,
+ heim_error_t *error)
+{
+ return heim_json_create_with_bytes(string, strlen(string), max_depth, flags,
+ error);
+}
+
+heim_object_t
+heim_json_create_with_bytes(const void *data, size_t length, size_t max_depth,
+ heim_json_flags_t flags, heim_error_t *error)
+{
+ struct parse_ctx ctx;
+ heim_object_t o;
+
+ heim_base_once_f(&heim_json_once, NULL, json_init_once);
+
+ ctx.lineno = 1;
+ ctx.p = data;
+ ctx.pstart = data;
+ ctx.pend = ((uint8_t *)data) + length;
+ ctx.error = NULL;
+ ctx.flags = flags;
+ ctx.depth = max_depth;
+
+ o = parse_value(&ctx);
+
+ if (o == NULL && error) {
+ *error = ctx.error;
+ } else if (ctx.error) {
+ heim_release(ctx.error);
+ }
+
+ return o;
+}
+
+
+static void
+show_printf(void *ctx, const char *str)
+{
+ if (str == NULL)
+ return;
+ fprintf(ctx, "%s", str);
+}
+
+/**
+ * Dump a heimbase object to stderr (useful from the debugger!)
+ *
+ * @param obj object to dump using JSON or JSON-like format
+ *
+ * @addtogroup heimbase
+ */
+void
+heim_show(heim_object_t obj)
+{
+ heim_base2json(obj, stderr, HEIM_JSON_F_NO_DATA_DICT, show_printf);
+}
+
+static void
+strbuf_add(void *ctx, const char *str)
+{
+ struct heim_strbuf *strbuf = ctx;
+ size_t len;
+
+ if (strbuf->enomem)
+ return;
+
+ if (str == NULL) {
+ /*
+ * Eat the last '\n'; this is used when formatting dict pairs
+ * and array items so that the ',' separating them is never
+ * preceded by a '\n'.
+ */
+ if (strbuf->len > 0 && strbuf->str[strbuf->len - 1] == '\n')
+ strbuf->len--;
+ return;
+ }
+
+ len = strlen(str);
+ if ((len + 1) > (strbuf->alloced - strbuf->len)) {
+ size_t new_len = strbuf->alloced + (strbuf->alloced >> 2) + len + 1;
+ char *s;
+
+ s = realloc(strbuf->str, new_len);
+ if (s == NULL) {
+ strbuf->enomem = 1;
+ return;
+ }
+ strbuf->str = s;
+ strbuf->alloced = new_len;
+ }
+ /* +1 so we copy the NUL */
+ (void) memcpy(strbuf->str + strbuf->len, str, len + 1);
+ strbuf->len += len;
+ if (strbuf->str[strbuf->len - 1] == '\n' &&
+ strbuf->flags & HEIM_JSON_F_ONE_LINE)
+ strbuf->len--;
+}
+
+#define STRBUF_INIT_SZ 64
+
+heim_string_t
+heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_t *error)
+{
+ heim_string_t str;
+ struct heim_strbuf strbuf;
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ memset(&strbuf, 0, sizeof (strbuf));
+ strbuf.str = malloc(STRBUF_INIT_SZ);
+ if (strbuf.str == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ strbuf.len = 0;
+ strbuf.alloced = STRBUF_INIT_SZ;
+ strbuf.str[0] = '\0';
+ strbuf.flags = flags;
+
+ ret = heim_base2json(obj, &strbuf, flags, strbuf_add);
+ if (ret || strbuf.enomem) {
+ if (error) {
+ if (strbuf.enomem || ret == ENOMEM)
+ *error = heim_error_create_enomem();
+ else
+ *error = heim_error_create(1, "Impossible to JSON-encode "
+ "object");
+ }
+ free(strbuf.str);
+ return NULL;
+ }
+ if (flags & HEIM_JSON_F_ONE_LINE) {
+ strbuf.flags &= ~HEIM_JSON_F_ONE_LINE;
+ strbuf_add(&strbuf, "\n");
+ }
+ str = heim_string_ref_create(strbuf.str, free);
+ if (str == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ free(strbuf.str);
+ }
+ return str;
+}
diff --git a/lib/base/null.c b/lib/base/null.c
new file mode 100644
index 000000000000..43bde965c777
--- /dev/null
+++ b/lib/base/null.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+
+struct heim_type_data _heim_null_object = {
+ HEIM_TID_NULL,
+ "null-object",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_null_t
+heim_null_create(void)
+{
+ return heim_base_make_tagged_object(0, HEIM_TID_NULL);
+}
diff --git a/lib/base/number.c b/lib/base/number.c
new file mode 100644
index 000000000000..c259f69971d0
--- /dev/null
+++ b/lib/base/number.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+
+static void
+number_dealloc(void *ptr)
+{
+}
+
+static int
+number_cmp(void *a, void *b)
+{
+ int na, nb;
+
+ if (heim_base_is_tagged_object(a))
+ na = heim_base_tagged_object_value(a);
+ else
+ na = *(int *)a;
+
+ if (heim_base_is_tagged_object(b))
+ nb = heim_base_tagged_object_value(b);
+ else
+ nb = *(int *)b;
+
+ return na - nb;
+}
+
+static unsigned long
+number_hash(void *ptr)
+{
+ if (heim_base_is_tagged_object(ptr))
+ return heim_base_tagged_object_value(ptr);
+ return (unsigned long)*(int *)ptr;
+}
+
+struct heim_type_data _heim_number_object = {
+ HEIM_TID_NUMBER,
+ "number-object",
+ NULL,
+ number_dealloc,
+ NULL,
+ number_cmp,
+ number_hash,
+ NULL
+};
+
+/**
+ * Create a number object
+ *
+ * @param the number to contain in the object
+ *
+ * @return a number object
+ */
+
+heim_number_t
+heim_number_create(int number)
+{
+ heim_number_t n;
+
+ if (number < 0xffffff && number >= 0)
+ return heim_base_make_tagged_object(number, HEIM_TID_NUMBER);
+
+ n = _heim_alloc_object(&_heim_number_object, sizeof(int));
+ if (n)
+ *((int *)n) = number;
+ return n;
+}
+
+/**
+ * Return the type ID of number objects
+ *
+ * @return type id of number objects
+ */
+
+heim_tid_t
+heim_number_get_type_id(void)
+{
+ return HEIM_TID_NUMBER;
+}
+
+/**
+ * Get the int value of the content
+ *
+ * @param number the number object to get the value from
+ *
+ * @return an int
+ */
+
+int
+heim_number_get_int(heim_number_t number)
+{
+ if (heim_base_is_tagged_object(number))
+ return heim_base_tagged_object_value(number);
+ return *(int *)number;
+}
diff --git a/lib/base/roken_rename.h b/lib/base/roken_rename.h
new file mode 100644
index 000000000000..ea72098933ac
--- /dev/null
+++ b/lib/base/roken_rename.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
+ */
+
+/* $Id$ */
+
+#ifndef __heimbase_roken_rename_h__
+#define __heimbase_roken_rename_h__
+
+#ifndef HAVE_VSNPRINTF
+#define rk_vsnprintf heimbase_vsnprintf
+#endif
+#ifndef HAVE_ASPRINTF
+#define rk_asprintf heimbase_asprintf
+#endif
+#ifndef HAVE_ASNPRINTF
+#define rk_asnprintf heimbase_asnprintf
+#endif
+#ifndef HAVE_VASPRINTF
+#define rk_vasprintf heimbase_vasprintf
+#endif
+#ifndef HAVE_VASNPRINTF
+#define rk_vasnprintf heimbase_vasnprintf
+#endif
+#ifndef HAVE_STRDUP
+#define rk_strdup heimbase_strdup
+#endif
+#ifndef HAVE_STRNDUP
+#define rk_strndup heimbase_strndup
+#endif
+
+#endif /* __heimbase_roken_rename_h__ */
diff --git a/lib/base/string.c b/lib/base/string.c
new file mode 100644
index 000000000000..35ea2182ba3e
--- /dev/null
+++ b/lib/base/string.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "baselocl.h"
+#include <string.h>
+
+static void
+string_dealloc(void *ptr)
+{
+ heim_string_t s = ptr;
+ heim_string_free_f_t *deallocp;
+ heim_string_free_f_t dealloc;
+
+ if (*(const char *)ptr != '\0')
+ return;
+
+ /* Possible string ref */
+ deallocp = _heim_get_isaextra(s, 0);
+ dealloc = *deallocp;
+ if (dealloc != NULL) {
+ char **strp = _heim_get_isaextra(s, 1);
+ dealloc(*strp);
+ }
+}
+
+static int
+string_cmp(void *a, void *b)
+{
+ if (*(char *)a == '\0') {
+ char **strp = _heim_get_isaextra(a, 1);
+
+ if (*strp != NULL)
+ a = *strp; /* a is a string ref */
+ }
+ if (*(char *)b == '\0') {
+ char **strp = _heim_get_isaextra(b, 1);
+
+ if (*strp != NULL)
+ b = *strp; /* b is a string ref */
+ }
+ return strcmp(a, b);
+}
+
+static unsigned long
+string_hash(void *ptr)
+{
+ const char *s = ptr;
+ unsigned long n;
+
+ for (n = 0; *s; ++s)
+ n += *s;
+ return n;
+}
+
+struct heim_type_data _heim_string_object = {
+ HEIM_TID_STRING,
+ "string-object",
+ NULL,
+ string_dealloc,
+ NULL,
+ string_cmp,
+ string_hash,
+ NULL
+};
+
+/**
+ * Create a string object
+ *
+ * @param string the string to create, must be an utf8 string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create(const char *string)
+{
+ return heim_string_create_with_bytes(string, strlen(string));
+}
+
+/**
+ * Create a string object without copying the source.
+ *
+ * @param string the string to referenced, must be UTF-8
+ * @param dealloc the function to use to release the referece to the string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_ref_create(const char *string, heim_string_free_f_t dealloc)
+{
+ heim_string_t s;
+ heim_string_free_f_t *deallocp;
+
+ s = _heim_alloc_object(&_heim_string_object, 1);
+ if (s) {
+ const char **strp;
+
+ ((char *)s)[0] = '\0';
+ deallocp = _heim_get_isaextra(s, 0);
+ *deallocp = dealloc;
+ strp = _heim_get_isaextra(s, 1);
+ *strp = string;
+ }
+ return s;
+}
+
+/**
+ * Create a string object
+ *
+ * @param string the string to create, must be an utf8 string
+ * @param len the length of the string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create_with_bytes(const void *data, size_t len)
+{
+ heim_string_t s;
+
+ s = _heim_alloc_object(&_heim_string_object, len + 1);
+ if (s) {
+ memcpy(s, data, len);
+ ((char *)s)[len] = '\0';
+ }
+ return s;
+}
+
+/**
+ * Create a string object using a format string
+ *
+ * @param fmt format string
+ * @param ...
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create_with_format(const char *fmt, ...)
+{
+ heim_string_t s;
+ char *str = NULL;
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret < 0 || str == NULL)
+ return NULL;
+
+ s = heim_string_ref_create(str, string_dealloc);
+ if (s == NULL)
+ free(str);
+ return s;
+}
+
+/**
+ * Return the type ID of string objects
+ *
+ * @return type id of string objects
+ */
+
+heim_tid_t
+heim_string_get_type_id(void)
+{
+ return HEIM_TID_STRING;
+}
+
+/**
+ * Get the string value of the content.
+ *
+ * @param string the string object to get the value from
+ *
+ * @return a utf8 string
+ */
+
+const char *
+heim_string_get_utf8(heim_string_t string)
+{
+ if (*(const char *)string == '\0') {
+ const char **strp;
+
+ /* String ref */
+ strp = _heim_get_isaextra(string, 1);
+ if (*strp != NULL)
+ return *strp;
+ }
+ return (const char *)string;
+}
+
+/*
+ *
+ */
+
+static void
+init_string(void *ptr)
+{
+ heim_dict_t *dict = ptr;
+ *dict = heim_dict_create(101);
+ heim_assert(*dict != NULL, "__heim_string_constant");
+}
+
+heim_string_t
+__heim_string_constant(const char *_str)
+{
+ static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
+ static heim_base_once_t once;
+ static heim_dict_t dict = NULL;
+ heim_string_t s, s2;
+
+ heim_base_once_f(&once, &dict, init_string);
+ s = heim_string_create(_str);
+
+ HEIMDAL_MUTEX_lock(&mutex);
+ s2 = heim_dict_get_value(dict, s);
+ if (s2) {
+ heim_release(s);
+ s = s2;
+ } else {
+ _heim_make_permanent(s);
+ heim_dict_set_value(dict, s, s);
+ }
+ HEIMDAL_MUTEX_unlock(&mutex);
+
+ return s;
+}
diff --git a/lib/base/test_base.c b/lib/base/test_base.c
new file mode 100644
index 000000000000..250708df2ef4
--- /dev/null
+++ b/lib/base/test_base.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright (c) 2010-2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
+ */
+
+/*
+ * This is a test of libheimbase functionality. If you make any changes
+ * to libheimbase or to this test you should run it under valgrind with
+ * the following options:
+ *
+ * -v --track-fds=yes --num-callers=30 --leak-check=full
+ *
+ * and make sure that there are no leaks that don't have
+ * __heim_string_constant() or heim_db_register() in their stack trace.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef WIN32
+#include <sys/file.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "baselocl.h"
+
+static void
+memory_free(heim_object_t obj)
+{
+}
+
+static int
+test_memory(void)
+{
+ void *ptr;
+
+ ptr = heim_alloc(10, "memory", memory_free);
+
+ heim_retain(ptr);
+ heim_release(ptr);
+
+ heim_retain(ptr);
+ heim_release(ptr);
+
+ heim_release(ptr);
+
+ ptr = heim_alloc(10, "memory", NULL);
+ heim_release(ptr);
+
+ return 0;
+}
+
+static int
+test_mutex(void)
+{
+ HEIMDAL_MUTEX m = HEIMDAL_MUTEX_INITIALIZER;
+
+ HEIMDAL_MUTEX_lock(&m);
+ HEIMDAL_MUTEX_unlock(&m);
+ HEIMDAL_MUTEX_destroy(&m);
+
+ HEIMDAL_MUTEX_init(&m);
+ HEIMDAL_MUTEX_lock(&m);
+ HEIMDAL_MUTEX_unlock(&m);
+ HEIMDAL_MUTEX_destroy(&m);
+
+ return 0;
+}
+
+static int
+test_rwlock(void)
+{
+ HEIMDAL_RWLOCK l = HEIMDAL_RWLOCK_INITIALIZER;
+
+ HEIMDAL_RWLOCK_rdlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_wrlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_trywrlock(&l) != 0)
+ err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_tryrdlock(&l))
+ err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_destroy(&l);
+
+ HEIMDAL_RWLOCK_init(&l);
+ HEIMDAL_RWLOCK_rdlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_wrlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_trywrlock(&l))
+ err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_tryrdlock(&l))
+ err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_destroy(&l);
+
+ return 0;
+}
+
+static int
+test_dict(void)
+{
+ heim_dict_t dict;
+ heim_number_t a1 = heim_number_create(1);
+ heim_string_t a2 = heim_string_create("hejsan");
+ heim_number_t a3 = heim_number_create(3);
+ heim_string_t a4 = heim_string_create("foosan");
+
+ dict = heim_dict_create(10);
+
+ heim_dict_set_value(dict, a1, a2);
+ heim_dict_set_value(dict, a3, a4);
+
+ heim_dict_delete_key(dict, a3);
+ heim_dict_delete_key(dict, a1);
+
+ heim_release(a1);
+ heim_release(a2);
+ heim_release(a3);
+ heim_release(a4);
+
+ heim_release(dict);
+
+ return 0;
+}
+
+static int
+test_auto_release(void)
+{
+ heim_auto_release_t ar1, ar2;
+ heim_number_t n1;
+ heim_string_t s1;
+
+ ar1 = heim_auto_release_create();
+
+ s1 = heim_string_create("hejsan");
+ heim_auto_release(s1);
+
+ n1 = heim_number_create(1);
+ heim_auto_release(n1);
+
+ ar2 = heim_auto_release_create();
+
+ n1 = heim_number_create(1);
+ heim_auto_release(n1);
+
+ heim_release(ar2);
+ heim_release(ar1);
+
+ return 0;
+}
+
+static int
+test_string(void)
+{
+ heim_string_t s1, s2;
+ const char *string = "hejsan";
+
+ s1 = heim_string_create(string);
+ s2 = heim_string_create(string);
+
+ if (heim_cmp(s1, s2) != 0) {
+ printf("the same string is not the same\n");
+ exit(1);
+ }
+
+ heim_release(s1);
+ heim_release(s2);
+
+ return 0;
+}
+
+static int
+test_error(void)
+{
+ heim_error_t e;
+ heim_string_t s;
+
+ e = heim_error_create(10, "foo: %s", "bar");
+ heim_assert(heim_error_get_code(e) == 10, "error_code != 10");
+
+ s = heim_error_copy_string(e);
+ heim_assert(strcmp(heim_string_get_utf8(s), "foo: bar") == 0, "msg wrong");
+
+ heim_release(s);
+ heim_release(e);
+
+ return 0;
+}
+
+static int
+test_json(void)
+{
+ static char *j[] = {
+ "{ \"k1\" : \"s1\", \"k2\" : \"s2\" }",
+ "{ \"k1\" : [\"s1\", \"s2\", \"s3\"], \"k2\" : \"s3\" }",
+ "{ \"k1\" : {\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"}, \"k5\" : \"s4\" }",
+ "[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, "
+ "null, true, false, 123456789, \"\"]",
+ " -1"
+ };
+ char *s;
+ size_t i, k;
+ heim_object_t o, o2;
+ heim_string_t k1 = heim_string_create("k1");
+
+ o = heim_json_create("\"string\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
+ heim_release(o);
+
+ o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
+ heim_release(o);
+
+ o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o);
+
+ o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
+ "{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o);
+
+ o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
+ "{ \"k3\" : \"s4\" } : -1 }", 10,
+ HEIM_JSON_F_STRICT_DICT, NULL);
+ heim_assert(o == NULL, "dict");
+
+ o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("-10", 10, 0, NULL);
+ heim_assert(o != NULL, "number");
+ heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ heim_release(o);
+
+ o = heim_json_create("99", 10, 0, NULL);
+ heim_assert(o != NULL, "number");
+ heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ heim_release(o);
+
+ o = heim_json_create(" [ 1 ]", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ heim_release(o);
+
+ o = heim_json_create(" [ -1 ]", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ heim_release(o);
+
+ for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
+ o = heim_json_create(j[i], 10, 0, NULL);
+ if (o == NULL) {
+ fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
+ return 1;
+ }
+ heim_release(o);
+ /* Simple fuzz test */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ o = heim_json_create_with_bytes(j[i], k, 10, 0, NULL);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %.*s\n", (int)k, j[i]);
+ return EINVAL;
+ }
+ }
+ /* Again, but this time make it so valgrind can find invalid accesses */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ s = strndup(j[i], k);
+ if (s == NULL)
+ return ENOMEM;
+ o = heim_json_create(s, 10, 0, NULL);
+ free(s);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
+ return EINVAL;
+ }
+ }
+ /* Again, but with no NUL termination */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ s = malloc(k);
+ if (s == NULL)
+ return ENOMEM;
+ memcpy(s, j[i], k);
+ o = heim_json_create_with_bytes(s, k, 10, 0, NULL);
+ free(s);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
+ return EINVAL;
+ }
+ }
+ }
+
+ heim_release(k1);
+
+ return 0;
+}
+
+static int
+test_path(void)
+{
+ heim_dict_t dict = heim_dict_create(11);
+ heim_string_t p1 = heim_string_create("abc");
+ heim_string_t p2a = heim_string_create("def");
+ heim_string_t p2b = heim_string_create("DEF");
+ heim_number_t p3 = heim_number_create(0);
+ heim_string_t p4a = heim_string_create("ghi");
+ heim_string_t p4b = heim_string_create("GHI");
+ heim_array_t a = heim_array_create();
+ heim_number_t l1 = heim_number_create(42);
+ heim_number_t l2 = heim_number_create(813);
+ heim_number_t l3 = heim_number_create(1234);
+ heim_string_t k1 = heim_string_create("k1");
+ heim_string_t k2 = heim_string_create("k2");
+ heim_string_t k3 = heim_string_create("k3");
+ heim_string_t k2_1 = heim_string_create("k2-1");
+ heim_string_t k2_2 = heim_string_create("k2-2");
+ heim_string_t k2_3 = heim_string_create("k2-3");
+ heim_string_t k2_4 = heim_string_create("k2-4");
+ heim_string_t k2_5 = heim_string_create("k2-5");
+ heim_string_t k2_5_1 = heim_string_create("k2-5-1");
+ heim_object_t o;
+ heim_object_t neg_num;
+ int ret;
+
+ if (!dict || !p1 || !p2a || !p2b || !p4a || !p4b)
+ return ENOMEM;
+
+ ret = heim_path_create(dict, 11, a, NULL, p1, p2a, NULL);
+ heim_release(a);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l3, NULL, p1, p2b, NULL);
+ if (ret)
+ return ret;
+ o = heim_path_get(dict, NULL, p1, p2b, NULL);
+ if (o != l3)
+ return 1;
+ ret = heim_path_create(dict, 11, NULL, NULL, p1, p2a, p3, NULL);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l1, NULL, p1, p2a, p3, p4a, NULL);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l2, NULL, p1, p2a, p3, p4b, NULL);
+ if (ret)
+ return ret;
+
+ o = heim_path_get(dict, NULL, p1, p2a, p3, p4a, NULL);
+ if (o != l1)
+ return 1;
+ o = heim_path_get(dict, NULL, p1, p2a, p3, p4b, NULL);
+ if (o != l2)
+ return 1;
+
+ heim_release(dict);
+
+ /* Test that JSON parsing works right by using heim_path_get() */
+ dict = heim_json_create("{\"k1\":1,"
+ "\"k2\":{\"k2-1\":21,"
+ "\"k2-2\":null,"
+ "\"k2-3\":true,"
+ "\"k2-4\":false,"
+ "\"k2-5\":[1,2,3,{\"k2-5-1\":-1},-2]},"
+ "\"k3\":[true,false,0,42]}", 10, 0, NULL);
+ heim_assert(dict != NULL, "dict");
+ o = heim_path_get(dict, NULL, k1, NULL);
+ if (heim_cmp(o, heim_number_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, NULL);
+ if (heim_get_tid(o) != heim_dict_get_type_id()) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_1, NULL);
+ if (heim_cmp(o, heim_number_create(21))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_2, NULL);
+ if (heim_cmp(o, heim_null_create())) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_3, NULL);
+ if (heim_cmp(o, heim_bool_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_4, NULL);
+ if (heim_cmp(o, heim_bool_create(0))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, NULL);
+ if (heim_get_tid(o) != heim_array_get_type_id()) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(0), NULL);
+ if (heim_cmp(o, heim_number_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(1), NULL);
+ if (heim_cmp(o, heim_number_create(2))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(3), k2_5_1, NULL);
+ if (heim_cmp(o, neg_num = heim_number_create(-1))) return 1;
+ heim_release(neg_num);
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(4), NULL);
+ if (heim_cmp(o, neg_num = heim_number_create(-2))) return 1;
+ heim_release(neg_num);
+ o = heim_path_get(dict, NULL, k3, heim_number_create(3), NULL);
+ if (heim_cmp(o, heim_number_create(42))) return 1;
+
+ heim_release(dict);
+ heim_release(p1);
+ heim_release(p2a);
+ heim_release(p2b);
+ heim_release(p4a);
+ heim_release(p4b);
+ heim_release(k1);
+ heim_release(k2);
+ heim_release(k3);
+ heim_release(k2_1);
+ heim_release(k2_2);
+ heim_release(k2_3);
+ heim_release(k2_4);
+ heim_release(k2_5);
+ heim_release(k2_5_1);
+
+ return 0;
+}
+
+typedef struct dict_db {
+ heim_dict_t dict;
+ int locked;
+} *dict_db_t;
+
+static int
+dict_db_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ dict_db_t dictdb;
+ heim_dict_t contents = NULL;
+
+ if (error)
+ *error = NULL;
+ if (dbtype && *dbtype && strcmp(dbtype, "dictdb"))
+ return EINVAL;
+ if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0)
+ return EINVAL;
+ dictdb = heim_alloc(sizeof (*dictdb), "dict_db", NULL);
+ if (dictdb == NULL)
+ return ENOMEM;
+
+ if (contents != NULL)
+ dictdb->dict = contents;
+ else {
+ dictdb->dict = heim_dict_create(29);
+ if (dictdb->dict == NULL) {
+ heim_release(dictdb);
+ return ENOMEM;
+ }
+ }
+
+ *db = dictdb;
+ return 0;
+}
+
+static int
+dict_db_close(void *db, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ heim_release(dictdb->dict);
+ heim_release(dictdb);
+ return 0;
+}
+
+static int
+dict_db_lock(void *db, int read_only, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ if (dictdb->locked)
+ return EWOULDBLOCK;
+ dictdb->locked = 1;
+ return 0;
+}
+
+static int
+dict_db_unlock(void *db, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ dictdb->locked = 0;
+ return 0;
+}
+
+static heim_data_t
+dict_db_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ return heim_retain(heim_path_get(dictdb->dict, error, table, key, NULL));
+}
+
+static int
+dict_db_set_value(void *db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ return heim_path_create(dictdb->dict, 29, value, error, table, key, NULL);
+}
+
+static int
+dict_db_del_key(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ heim_path_delete(dictdb->dict, error, table, key, NULL);
+ return 0;
+}
+
+struct dict_db_iter_ctx {
+ heim_db_iterator_f_t iter_f;
+ void *iter_ctx;
+};
+
+static void dict_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct dict_db_iter_ctx *ctx = arg;
+
+ ctx->iter_f((heim_object_t)key, (heim_object_t)value, ctx->iter_ctx);
+}
+
+static void
+dict_db_iter(void *db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+ struct dict_db_iter_ctx ctx;
+ heim_dict_t table_dict;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ table_dict = heim_dict_copy_value(dictdb->dict, table);
+ if (table_dict == NULL)
+ return;
+
+ ctx.iter_ctx = iter_data;
+ ctx.iter_f = iter_f;
+
+ heim_dict_iterate_f(table_dict, &ctx, dict_db_iter_f);
+ heim_release(table_dict);
+}
+
+static void
+test_db_iter(heim_data_t k, heim_data_t v, void *arg)
+{
+ int *ret = arg;
+ const void *kptr, *vptr;
+ size_t klen, vlen;
+
+ heim_assert(heim_get_tid(k) == heim_data_get_type_id(), "...");
+
+ kptr = heim_data_get_ptr(k);
+ klen = heim_data_get_length(k);
+ vptr = heim_data_get_ptr(v);
+ vlen = heim_data_get_length(v);
+
+ if (klen == strlen("msg") && !strncmp(kptr, "msg", strlen("msg")) &&
+ vlen == strlen("abc") && !strncmp(vptr, "abc", strlen("abc")))
+ *ret &= ~(1);
+ else if (klen == strlen("msg2") &&
+ !strncmp(kptr, "msg2", strlen("msg2")) &&
+ vlen == strlen("FooBar") && !strncmp(vptr, "FooBar", strlen("FooBar")))
+ *ret &= ~(2);
+ else
+ *ret |= 4;
+}
+
+static struct heim_db_type dbt = {
+ 1, dict_db_open, NULL, dict_db_close,
+ dict_db_lock, dict_db_unlock, NULL, NULL, NULL, NULL,
+ dict_db_copy_value, dict_db_set_value,
+ dict_db_del_key, dict_db_iter
+};
+
+static int
+test_db(const char *dbtype, const char *dbname)
+{
+ heim_data_t k1, k2, v, v1, v2, v3;
+ heim_db_t db;
+ int ret;
+
+ if (dbtype == NULL) {
+ ret = heim_db_register("dictdb", NULL, &dbt);
+ heim_assert(!ret, "...");
+ db = heim_db_create("dictdb", "foo", NULL, NULL);
+ heim_assert(!db, "...");
+ db = heim_db_create("foobar", "MEMORY", NULL, NULL);
+ heim_assert(!db, "...");
+ db = heim_db_create("dictdb", "MEMORY", NULL, NULL);
+ heim_assert(db, "...");
+ } else {
+ heim_dict_t options;
+
+ options = heim_dict_create(11);
+ if (options == NULL) return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("journal-filename"),
+ HSTR("json-journal")))
+ return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("create"), heim_null_create()))
+ return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("truncate"), heim_null_create()))
+ return ENOMEM;
+ db = heim_db_create(dbtype, dbname, options, NULL);
+ heim_assert(db, "...");
+ heim_release(options);
+ }
+
+ k1 = heim_data_create("msg", strlen("msg"));
+ k2 = heim_data_create("msg2", strlen("msg2"));
+ v1 = heim_data_create("Hello world!", strlen("Hello world!"));
+ v2 = heim_data_create("FooBar", strlen("FooBar"));
+ v3 = heim_data_create("abc", strlen("abc"));
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_set_value(db, NULL, k2, v2, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k2, NULL);
+ heim_assert(v && !heim_cmp(v, v2), "...");
+ heim_release(v);
+
+ ret = heim_db_set_value(db, NULL, k1, v3, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v3), "...");
+ heim_release(v);
+
+ ret = 3;
+ heim_db_iterate_f(db, NULL, &ret, test_db_iter, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_commit(db, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v3), "...");
+ heim_release(v);
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_commit(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_delete_key(db, NULL, k1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v == NULL, "...");
+ heim_release(v);
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ if (dbtype != NULL) {
+ heim_data_t k3 = heim_data_create("value-is-a-dict", strlen("value-is-a-dict"));
+ heim_dict_t vdict = heim_dict_create(11);
+ heim_db_t db2;
+
+ heim_assert(k3 && vdict, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k1"), heim_number_create(11));
+ heim_assert(!ret, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k2"), heim_null_create());
+ heim_assert(!ret, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k3"), HSTR("a value"));
+ heim_assert(!ret, "...");
+ ret = heim_db_set_value(db, NULL, k3, (heim_data_t)vdict, NULL);
+ heim_assert(!ret, "...");
+
+ heim_release(vdict);
+
+ db2 = heim_db_create(dbtype, dbname, NULL, NULL);
+ heim_assert(db2, "...");
+
+ vdict = (heim_dict_t)heim_db_copy_value(db2, NULL, k3, NULL);
+ heim_release(db2);
+ heim_release(k3);
+ heim_assert(vdict, "...");
+ heim_assert(heim_get_tid(vdict) == heim_dict_get_type_id(), "...");
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k1"));
+ heim_assert(v && !heim_cmp(v, heim_number_create(11)), "...");
+ heim_release(v);
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k2"));
+ heim_assert(v && !heim_cmp(v, heim_null_create()), "...");
+ heim_release(v);
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k3"));
+ heim_assert(v && !heim_cmp(v, HSTR("a value")), "...");
+ heim_release(v);
+
+ heim_release(vdict);
+ }
+
+ heim_release(db);
+ heim_release(k1);
+ heim_release(k2);
+ heim_release(v1);
+ heim_release(v2);
+ heim_release(v3);
+
+ return 0;
+}
+
+struct test_array_iter_ctx {
+ char buf[256];
+};
+
+static void test_array_iter(heim_object_t elt, void *arg, int *stop)
+{
+ struct test_array_iter_ctx *iter_ctx = arg;
+
+ strcat(iter_ctx->buf, heim_string_get_utf8((heim_string_t)elt));
+}
+
+static int
+test_array()
+{
+ struct test_array_iter_ctx iter_ctx;
+ heim_string_t s1 = heim_string_create("abc");
+ heim_string_t s2 = heim_string_create("def");
+ heim_string_t s3 = heim_string_create("ghi");
+ heim_string_t s4 = heim_string_create("jkl");
+ heim_string_t s5 = heim_string_create("mno");
+ heim_string_t s6 = heim_string_create("pqr");
+ heim_array_t a = heim_array_create();
+
+ if (!s1 || !s2 || !s3 || !s4 || !s5 || !s6 || !a)
+ return ENOMEM;
+
+ heim_array_append_value(a, s4);
+ heim_array_append_value(a, s5);
+ heim_array_insert_value(a, 0, s3);
+ heim_array_insert_value(a, 0, s2);
+ heim_array_append_value(a, s6);
+ heim_array_insert_value(a, 0, s1);
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefghijklmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefjklmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 0);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defmno") != 0)
+ return 1;
+
+ heim_array_insert_value(a, 0, s1);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefmno") != 0)
+ return 1;
+
+ heim_array_insert_value(a, 0, s2);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmno") != 0)
+ return 1;
+
+ heim_array_append_value(a, s3);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmnoghi") != 0)
+ return 1;
+
+ heim_array_append_value(a, s6);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmnoghipqr") != 0)
+ return 1;
+
+ heim_release(s1);
+ heim_release(s2);
+ heim_release(s3);
+ heim_release(s4);
+ heim_release(s5);
+ heim_release(s6);
+ heim_release(a);
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int res = 0;
+
+ res |= test_memory();
+ res |= test_mutex();
+ res |= test_rwlock();
+ res |= test_dict();
+ res |= test_auto_release();
+ res |= test_string();
+ res |= test_error();
+ res |= test_json();
+ res |= test_path();
+ res |= test_db(NULL, NULL);
+ res |= test_db("json", argc > 1 ? argv[1] : "test_db.json");
+ res |= test_array();
+
+ return res ? 1 : 0;
+}
diff --git a/lib/base/version-script.map b/lib/base/version-script.map
new file mode 100644
index 000000000000..656277e37da2
--- /dev/null
+++ b/lib/base/version-script.map
@@ -0,0 +1,94 @@
+
+HEIMDAL_BASE_1.0 {
+ global:
+ _bsearch_file;
+ _bsearch_file_close;
+ _bsearch_file_info;
+ _bsearch_file_open;
+ _bsearch_text;
+ __heim_string_constant;
+ DllMain;
+ heim_abort;
+ heim_abortv;
+ heim_alloc;
+ heim_array_append_value;
+ heim_array_copy_value;
+ heim_array_create;
+ heim_array_delete_value;
+ heim_array_filter_f;
+ heim_array_get_length;
+ heim_array_get_type_id;
+ heim_array_get_value;
+ heim_array_iterate_f;
+ heim_array_iterate_reverse_f;
+ heim_array_insert_value;
+ heim_array_set_value;
+ heim_auto_release;
+ heim_auto_release_create;
+ heim_auto_release_drain;
+ heim_base_once_f;
+ heim_bool_create;
+ heim_bool_val;
+ heim_cmp;
+ heim_data_create;
+ heim_data_ref_create;
+ heim_data_get_data;
+ heim_data_get_length;
+ heim_data_get_ptr;
+ heim_data_get_type_id;
+ heim_data_ref_get_type_id;
+ heim_db_begin;
+ heim_db_clone;
+ heim_db_commit;
+ heim_db_copy_value;
+ heim_db_delete_key;
+ heim_db_get_type_id;
+ heim_db_iterate_f;
+ heim_db_create;
+ heim_db_register;
+ heim_db_rollback;
+ heim_db_set_value;
+ heim_dict_copy_value;
+ heim_dict_create;
+ heim_dict_delete_key;
+ heim_dict_get_type_id;
+ heim_dict_get_value;
+ heim_dict_iterate_f;
+ heim_dict_set_value;
+ heim_error_append;
+ heim_error_copy_string;
+ heim_error_create_opt;
+ heim_error_create;
+ heim_error_createv;
+ heim_error_create_enomem;
+ heim_error_get_code;
+ heim_get_hash;
+ heim_get_tid;
+ heim_json_create;
+ heim_json_create_with_bytes;
+ heim_json_copy_serialize;
+ heim_null_create;
+ heim_number_create;
+ heim_number_get_int;
+ heim_number_get_type_id;
+ heim_path_create;
+ heim_path_delete;
+ heim_path_get;
+ heim_path_copy;
+ heim_path_vcreate;
+ heim_path_vdelete;
+ heim_path_vget;
+ heim_path_vcopy;
+ heim_release;
+ heim_retain;
+ heim_show;
+ heim_sorted_text_file_dbtype;
+ heim_string_create;
+ heim_string_create_with_bytes;
+ heim_string_create_with_format;
+ heim_string_get_type_id;
+ heim_string_get_utf8;
+ heim_string_ref_create;
+ local:
+ *;
+};