diff options
author | Hiroki Sato <hrs@FreeBSD.org> | 2018-04-04 04:21:19 +0000 |
---|---|---|
committer | Hiroki Sato <hrs@FreeBSD.org> | 2018-04-04 04:21:19 +0000 |
commit | d684f11da759490a8d98d7b790796106285f4084 (patch) | |
tree | 27b7356df710fdf1440fe2c23154b8121e99f2ab /lib/base | |
parent | f52d4664e3f68828c06f85bfc1afa271e3e04713 (diff) | |
download | src-d684f11da759490a8d98d7b790796106285f4084.tar.gz src-d684f11da759490a8d98d7b790796106285f4084.zip |
Import Heimdal 7.5.0.vendor/heimdal/7.5.0vendor/heimdal
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.am | 64 | ||||
-rw-r--r-- | lib/base/Makefile.in | 1482 | ||||
-rw-r--r-- | lib/base/NTMakefile | 82 | ||||
-rw-r--r-- | lib/base/array.c | 478 | ||||
-rw-r--r-- | lib/base/baselocl.h | 167 | ||||
-rw-r--r-- | lib/base/bool.c | 59 | ||||
-rw-r--r-- | lib/base/bsearch.c | 886 | ||||
-rw-r--r-- | lib/base/data.c | 165 | ||||
-rw-r--r-- | lib/base/db.c | 1727 | ||||
-rw-r--r-- | lib/base/dict.c | 303 | ||||
-rw-r--r-- | lib/base/dll.c | 324 | ||||
-rw-r--r-- | lib/base/error.c | 178 | ||||
-rw-r--r-- | lib/base/heimbase.c | 1079 | ||||
-rw-r--r-- | lib/base/heimbase.h | 431 | ||||
-rw-r--r-- | lib/base/heimbasepriv.h | 113 | ||||
-rw-r--r-- | lib/base/heimqueue.h | 167 | ||||
-rw-r--r-- | lib/base/json.c | 811 | ||||
-rw-r--r-- | lib/base/null.c | 53 | ||||
-rw-r--r-- | lib/base/number.c | 128 | ||||
-rw-r--r-- | lib/base/roken_rename.h | 61 | ||||
-rw-r--r-- | lib/base/string.c | 260 | ||||
-rw-r--r-- | lib/base/test_base.c | 961 | ||||
-rw-r--r-- | lib/base/version-script.map | 94 |
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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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: + *; +}; |