aboutsummaryrefslogtreecommitdiff
path: root/lib/base
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2023-06-26 22:56:52 +0000
committerCy Schubert <cy@FreeBSD.org>2023-06-26 22:56:52 +0000
commitb6a943f7197af1a5eb6bb028b9b808ec5016e30c (patch)
treecfbb91e940dd89d0e1d46095f43c228d7d079fa0 /lib/base
parent6f4e10db3298f6d65e1e646fe52aaafc3682b788 (diff)
Diffstat (limited to 'lib/base')
-rw-r--r--lib/base/Makefile.am47
-rw-r--r--lib/base/Makefile.in1567
-rw-r--r--lib/base/NTMakefile60
-rw-r--r--lib/base/array.c4
-rw-r--r--lib/base/baselocl.h107
-rw-r--r--lib/base/common_plugin.h106
-rw-r--r--lib/base/config_file.c1472
-rw-r--r--lib/base/config_reg.c658
-rw-r--r--lib/base/context.c394
-rw-r--r--lib/base/data.c9
-rw-r--r--lib/base/db.c31
-rw-r--r--lib/base/dict.c8
-rw-r--r--lib/base/error.c7
-rw-r--r--lib/base/error_string.c177
-rw-r--r--lib/base/expand_path.c725
-rw-r--r--lib/base/heim_err.et57
-rw-r--r--lib/base/heimbase-atomics.h386
-rw-r--r--lib/base/heimbase-svc.h83
-rw-r--r--lib/base/heimbase.c111
-rw-r--r--lib/base/heimbase.h301
-rw-r--r--lib/base/heimbasepriv.h28
-rw-r--r--lib/base/heimqueue.h167
-rw-r--r--lib/base/json.c864
-rw-r--r--lib/base/log.c1079
-rw-r--r--lib/base/number.c22
-rw-r--r--lib/base/plugin.c785
-rw-r--r--lib/base/string.c13
-rw-r--r--lib/base/test_base.c437
-rw-r--r--lib/base/version-script.map137
-rw-r--r--lib/base/warn.c169
30 files changed, 7765 insertions, 2246 deletions
diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am
index c5f857f4daeb..606e00a975c4 100644
--- a/lib/base/Makefile.am
+++ b/lib/base/Makefile.am
@@ -11,7 +11,7 @@ IMPLEMENT_TLS += dll.c
AM_CPPFLAGS += -DHEIM_BASE_MAINTAINER
endif
-AM_CPPFLAGS += $(ROKEN_RENAME)
+AM_CPPFLAGS += $(ROKEN_RENAME) -I../com_err -I$(srcdir)/../com_err
lib_LTLIBRARIES = libheimbase.la
check_PROGRAMS = test_base
@@ -20,36 +20,61 @@ test_base_CFLAGS = -Wno-string-concatenation
libheimbase_la_LDFLAGS = -version-info 1:0:0
+if FRAMEWORK_COREFOUNDATION
+libheimbase_la_LDFLAGS += -framework CoreFoundation
+endif
+
TESTS = test_base
if versionscript
libheimbase_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
endif
-libheimbase_la_LIBADD = $(PTHREAD_LIBADD)
+libheimbase_la_LIBADD = $(PTHREAD_LIBADD) $(LIB_dlopen) $(LIB_com_err)
+
+include_HEADERS = heimbase.h common_plugin.h heimbase-atomics.h heimbase-svc.h
-include_HEADERS = heimbase.h
+ERR_FILES = heim_err.c
dist_libheimbase_la_SOURCES = \
array.c \
baselocl.h \
bsearch.c \
bool.c \
+ common_plugin.h \
+ config_file.c \
+ context.c \
data.c \
db.c \
dict.c \
$(IMPLEMENT_TLS) \
error.c \
+ error_string.c \
+ expand_path.c \
heimbase.c \
heimbasepriv.h \
- heimqueue.h \
json.c \
+ log.c \
null.c \
number.c \
+ plugin.c \
roken_rename.h \
- string.c
+ string.c \
+ warn.c
+
+nodist_libheimbase_la_SOURCES = $(ES) $(ERR_FILES)
+
+$(libheimbase_la_OBJECTS): $(srcdir)/heimbase-protos.h heim_err.h
-nodist_libheimbase_la_SOURCES = $(ES)
+$(srcdir)/heimbase-protos.h: $(dist_libheimbase_la_SOURCES)
+ cd $(srcdir) && \
+ perl ../../cf/make-proto.pl -q -P comment -o heimbase-protos.h $(dist_libheimbase_la_SOURCES) || \
+ rm -f heimbase-protos.h
+
+$(srcdir)/heimbase-private.h: $(dist_libheimbase_la_SOURCES)
+ cd $(srcdir) && \
+ perl ../../cf/make-proto.pl -q -P comment -p heimbase-private.h $(dist_libheimbase_la_SOURCES) || \
+ rm -f heimbase-private.h
# install these?
@@ -57,9 +82,15 @@ libheimbase_la_DEPENDENCIES = version-script.map
test_base_LDADD = libheimbase.la $(LIB_roken)
-CLEANFILES = base64.c test_db.json
+CLEANFILES = base64.c test_db.json heim_err.c heim_err.h
+
+EXTRA_DIST = NTMakefile version-script.map config_reg.c heim_err.et
+
+dist_include_HEADERS = heimbase-protos.h
+
+nodist_include_HEADERS = heim_err.h
-EXTRA_DIST = NTMakefile version-script.map
+heim_err.h: heim_err.et
base64.c:
rm -f base64.c
diff --git a/lib/base/Makefile.in b/lib/base/Makefile.in
deleted file mode 100644
index b46043ee2c56..000000000000
--- a/lib/base/Makefile.in
+++ /dev/null
@@ -1,1567 +0,0 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994-2021 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-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-test_base.$(OBJEXT)
-test_base_DEPENDENCIES = libheimbase.la $(am__DEPENDENCIES_1)
-test_base_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_base_CFLAGS) \
- $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
-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__maybe_remake_depfiles = depfiles
-am__depfiles_remade = ./$(DEPDIR)/array.Plo ./$(DEPDIR)/base64.Plo \
- ./$(DEPDIR)/bool.Plo ./$(DEPDIR)/bsearch.Plo \
- ./$(DEPDIR)/data.Plo ./$(DEPDIR)/db.Plo ./$(DEPDIR)/dict.Plo \
- ./$(DEPDIR)/dll.Plo ./$(DEPDIR)/error.Plo \
- ./$(DEPDIR)/heimbase.Plo ./$(DEPDIR)/json.Plo \
- ./$(DEPDIR)/null.Plo ./$(DEPDIR)/number.Plo \
- ./$(DEPDIR)/string.Plo ./$(DEPDIR)/test_base-test_base.Po
-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)`
-am__tty_colors_dummy = \
- mgn= red= grn= lgn= blu= brg= std=; \
- am__color_tests=no
-am__tty_colors = { \
- $(am__tty_colors_dummy); \
- if test "X$(AM_COLOR_TESTS)" = Xno; then \
- am__color_tests=no; \
- elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
- am__color_tests=yes; \
- elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
- am__color_tests=yes; \
- fi; \
- if test $$am__color_tests = yes; then \
- red=''; \
- grn=''; \
- lgn=''; \
- blu=''; \
- mgn=''; \
- brg=''; \
- std=''; \
- fi; \
-}
-am__recheck_rx = ^[ ]*:recheck:[ ]*
-am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
-am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
-# A command that, given a newline-separated list of test names on the
-# standard input, print the name of the tests that are to be re-run
-# upon "make recheck".
-am__list_recheck_tests = $(AWK) '{ \
- recheck = 1; \
- while ((rc = (getline line < ($$0 ".trs"))) != 0) \
- { \
- if (rc < 0) \
- { \
- if ((getline line2 < ($$0 ".log")) < 0) \
- recheck = 0; \
- break; \
- } \
- else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
- { \
- recheck = 0; \
- break; \
- } \
- else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
- { \
- break; \
- } \
- }; \
- if (recheck) \
- print $$0; \
- close ($$0 ".trs"); \
- close ($$0 ".log"); \
-}'
-# A command that, given a newline-separated list of test names on the
-# standard input, create the global log from their .trs and .log files.
-am__create_global_log = $(AWK) ' \
-function fatal(msg) \
-{ \
- print "fatal: making $@: " msg | "cat >&2"; \
- exit 1; \
-} \
-function rst_section(header) \
-{ \
- print header; \
- len = length(header); \
- for (i = 1; i <= len; i = i + 1) \
- printf "="; \
- printf "\n\n"; \
-} \
-{ \
- copy_in_global_log = 1; \
- global_test_result = "RUN"; \
- while ((rc = (getline line < ($$0 ".trs"))) != 0) \
- { \
- if (rc < 0) \
- fatal("failed to read from " $$0 ".trs"); \
- if (line ~ /$(am__global_test_result_rx)/) \
- { \
- sub("$(am__global_test_result_rx)", "", line); \
- sub("[ ]*$$", "", line); \
- global_test_result = line; \
- } \
- else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
- copy_in_global_log = 0; \
- }; \
- if (copy_in_global_log) \
- { \
- rst_section(global_test_result ": " $$0); \
- while ((rc = (getline line < ($$0 ".log"))) != 0) \
- { \
- if (rc < 0) \
- fatal("failed to read from " $$0 ".log"); \
- print line; \
- }; \
- printf "\n"; \
- }; \
- close ($$0 ".trs"); \
- close ($$0 ".log"); \
-}'
-# Restructured Text title.
-am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
-# Solaris 10 'make', and several other traditional 'make' implementations,
-# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
-# by disabling -e (using the XSI extension "set +e") if it's set.
-am__sh_e_setup = case $$- in *e*) set +e;; esac
-# Default flags passed to test drivers.
-am__common_driver_flags = \
- --color-tests "$$am__color_tests" \
- --enable-hard-errors "$$am__enable_hard_errors" \
- --expect-failure "$$am__expect_failure"
-# To be inserted before the command running the test. Creates the
-# directory for the log if needed. Stores in $dir the directory
-# containing $f, in $tst the test, in $log the log. Executes the
-# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
-# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
-# will run the test scripts (or their associated LOG_COMPILER, if
-# thy have one).
-am__check_pre = \
-$(am__sh_e_setup); \
-$(am__vpath_adj_setup) $(am__vpath_adj) \
-$(am__tty_colors); \
-srcdir=$(srcdir); export srcdir; \
-case "$@" in \
- */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
- *) am__odir=.;; \
-esac; \
-test "x$$am__odir" = x"." || test -d "$$am__odir" \
- || $(MKDIR_P) "$$am__odir" || exit $$?; \
-if test -f "./$$f"; then dir=./; \
-elif test -f "$$f"; then dir=; \
-else dir="$(srcdir)/"; fi; \
-tst=$$dir$$f; log='$@'; \
-if test -n '$(DISABLE_HARD_ERRORS)'; then \
- am__enable_hard_errors=no; \
-else \
- am__enable_hard_errors=yes; \
-fi; \
-case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
- am__expect_failure=yes;; \
- *) \
- am__expect_failure=no;; \
-esac; \
-$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
-# A shell command to get the names of the tests scripts with any registered
-# extension removed (i.e., equivalently, the names of the test logs, with
-# the '.log' extension removed). The result is saved in the shell variable
-# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
-# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
-# since that might cause problem with VPATH rewrites for suffix-less tests.
-# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
-am__set_TESTS_bases = \
- bases='$(TEST_LOGS)'; \
- bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
- bases=`echo $$bases`
-AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
-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@
-CLANG_FORMAT = @CLANG_FORMAT@
-COMPILE_ET = @COMPILE_ET@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CSCOPE = @CSCOPE@
-CTAGS = @CTAGS@
-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@
-ETAGS = @ETAGS@
-EXEEXT = @EXEEXT@
-FGREP = @FGREP@
-FILECMD = @FILECMD@
-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@
-PYTHON = @PYTHON@
-PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
-PYTHON_PLATFORM = @PYTHON_PLATFORM@
-PYTHON_PREFIX = @PYTHON_PREFIX@
-PYTHON_VERSION = @PYTHON_VERSION@
-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@
-pkgpyexecdir = @pkgpyexecdir@
-pkgpythondir = @pkgpythondir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-pyexecdir = @pyexecdir@
-pythondir = @pythondir@
-runstatedir = @runstatedir@
-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
-test_base_CFLAGS = -Wno-string-concatenation
-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__maybe_remake_depfiles)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
- 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):
-
-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
-
-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)
-
-test_base$(EXEEXT): $(test_base_OBJECTS) $(test_base_DEPENDENCIES) $(EXTRA_test_base_DEPENDENCIES)
- @rm -f test_base$(EXEEXT)
- $(AM_V_CCLD)$(test_base_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@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bool.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bsearch.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dll.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heimbase.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/null.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/number.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_base-test_base.Po@am__quote@ # am--include-marker
-
-$(am__depfiles_remade):
- @$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
-
-am--depfiles: $(am__depfiles_remade)
-
-.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 $@ $<
-
-test_base-test_base.o: test_base.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -MT test_base-test_base.o -MD -MP -MF $(DEPDIR)/test_base-test_base.Tpo -c -o test_base-test_base.o `test -f 'test_base.c' || echo '$(srcdir)/'`test_base.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_base-test_base.Tpo $(DEPDIR)/test_base-test_base.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_base.c' object='test_base-test_base.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -c -o test_base-test_base.o `test -f 'test_base.c' || echo '$(srcdir)/'`test_base.c
-
-test_base-test_base.obj: test_base.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -MT test_base-test_base.obj -MD -MP -MF $(DEPDIR)/test_base-test_base.Tpo -c -o test_base-test_base.obj `if test -f 'test_base.c'; then $(CYGPATH_W) 'test_base.c'; else $(CYGPATH_W) '$(srcdir)/test_base.c'; fi`
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_base-test_base.Tpo $(DEPDIR)/test_base-test_base.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_base.c' object='test_base-test_base.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_base_CFLAGS) $(CFLAGS) -c -o test_base-test_base.obj `if test -f 'test_base.c'; then $(CYGPATH_W) 'test_base.c'; else $(CYGPATH_W) '$(srcdir)/test_base.c'; fi`
-
-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"$(AM_TESTSUITE_SUMMARY_HEADER)"$${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: $(check_PROGRAMS)
- @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: $(BUILT_SOURCES)
- $(MAKE) $(AM_MAKEFLAGS) distdir-am
-
-distdir-am: $(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
-install-checkPROGRAMS: install-libLTLIBRARIES
-
-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 -f ./$(DEPDIR)/array.Plo
- -rm -f ./$(DEPDIR)/base64.Plo
- -rm -f ./$(DEPDIR)/bool.Plo
- -rm -f ./$(DEPDIR)/bsearch.Plo
- -rm -f ./$(DEPDIR)/data.Plo
- -rm -f ./$(DEPDIR)/db.Plo
- -rm -f ./$(DEPDIR)/dict.Plo
- -rm -f ./$(DEPDIR)/dll.Plo
- -rm -f ./$(DEPDIR)/error.Plo
- -rm -f ./$(DEPDIR)/heimbase.Plo
- -rm -f ./$(DEPDIR)/json.Plo
- -rm -f ./$(DEPDIR)/null.Plo
- -rm -f ./$(DEPDIR)/number.Plo
- -rm -f ./$(DEPDIR)/string.Plo
- -rm -f ./$(DEPDIR)/test_base-test_base.Po
- -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 -f ./$(DEPDIR)/array.Plo
- -rm -f ./$(DEPDIR)/base64.Plo
- -rm -f ./$(DEPDIR)/bool.Plo
- -rm -f ./$(DEPDIR)/bsearch.Plo
- -rm -f ./$(DEPDIR)/data.Plo
- -rm -f ./$(DEPDIR)/db.Plo
- -rm -f ./$(DEPDIR)/dict.Plo
- -rm -f ./$(DEPDIR)/dll.Plo
- -rm -f ./$(DEPDIR)/error.Plo
- -rm -f ./$(DEPDIR)/heimbase.Plo
- -rm -f ./$(DEPDIR)/json.Plo
- -rm -f ./$(DEPDIR)/null.Plo
- -rm -f ./$(DEPDIR)/number.Plo
- -rm -f ./$(DEPDIR)/string.Plo
- -rm -f ./$(DEPDIR)/test_base-test_base.Po
- -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 am--depfiles 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
-
-# It's useful for debugging to format generated sources. The default for all
-# clang-format styles is to sort includes, but in many cases in-tree we really
-# don't want to do that.
-.x.c:
- @if [ -z "$(CLANG_FORMAT)" ]; then \
- cmp -s $< $@ 2> /dev/null || cp $< $@; \
- else \
- cp $< $@.tmp.c; \
- $(CLANG_FORMAT) -style='{BasedOnStyle: Chromium, SortIncludes: false}' -i $@.tmp.c; \
- cmp -s $@.tmp.c $@ 2> /dev/null || mv $@.tmp.c $@; \
- fi
-
-.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
index e5bda31dd722..49cd8b865199 100644
--- a/lib/base/NTMakefile
+++ b/lib/base/NTMakefile
@@ -35,31 +35,83 @@ intcflags=-I$(SRCDIR) -I$(OBJ)
!include ../../windows/NTMakefile.w32
-INCFILES=$(INCDIR)\heimbase.h
+INCFILES= \
+ $(INCDIR)\heimbase.h \
+ $(INCDIR)\heimbase-protos.h \
+ $(INCDIR)\heimbase-atomics.h \
+ $(INCDIR)\heimbase-svc.h \
+ $(INCDIR)\heim_err.h \
+ $(INCDIR)\common_plugin.h
test_binaries = $(OBJ)\test_base.exe
+libheimbase_SOURCES = \
+ array.c \
+ bool.c \
+ bsearch.c \
+ config_file.c \
+ config_reg.c \
+ context.c \
+ data.c \
+ db.c \
+ dict.c \
+ dll.c \
+ error.c \
+ error_string.c \
+ expand_path.c \
+ heimbase.c \
+ json.c \
+ log.c \
+ null.c \
+ number.c \
+ plugin.c \
+ string.c \
+ warn.c
+
libheimbase_OBJS = \
$(OBJ)\array.obj \
$(OBJ)\bool.obj \
$(OBJ)\bsearch.obj \
+ $(OBJ)\config_file.obj \
+ $(OBJ)\config_reg.obj \
+ $(OBJ)\context.obj \
$(OBJ)\data.obj \
$(OBJ)\db.obj \
$(OBJ)\dict.obj \
$(OBJ)\dll.obj \
$(OBJ)\error.obj \
+ $(OBJ)\error_string.obj \
+ $(OBJ)\expand_path.obj \
$(OBJ)\heimbase.obj \
$(OBJ)\json.obj \
+ $(OBJ)\log.obj \
$(OBJ)\null.obj \
$(OBJ)\number.obj \
- $(OBJ)\string.obj
+ $(OBJ)\plugin.obj \
+ $(OBJ)\string.obj \
+ $(OBJ)\warn.obj
+
+libheimbase_gen_OBJS= $(OBJ)\heim_err.obj
-$(LIBHEIMBASE): $(libheimbase_OBJS)
- $(LIBCON_C) -OUT:$@ $(LIBROKEN) @<<
+$(LIBHEIMBASE): $(libheimbase_OBJS) $(libheimbase_gen_OBJS)
+ $(LIBCON_C) -OUT:$@ $(LIBROKEN) $(LIBCOMERR) $(PTHREAD_LIB) Secur32.lib Shell32.lib Advapi32.lib Shlwapi.lib @<<
$(libheimbase_OBJS: =
)
+$(libheimbase_gen_OBJS: =
+)
<<
+$(OBJ)\heimbase-protos.h: $(libheimbase_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl -E KRB5_LIB -q -P remove -o $(OBJ)\heimbase-protos.h $(libheimbase_SOURCES) || $(RM) -f $(OBJ)\heimbase-protos.h
+
+$(OBJ)\heimbase-private.h: $(libheimbase_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl -q -P remove -p $(OBJ)\heimbase-private.h $(libheimbase_SOURCES) || $(RM) -f $(OBJ)\heimbase-private.h
+
+$(OBJ)\heim_err.c $(OBJ)\heim_err.h: heim_err.et
+ cd $(OBJ)
+ $(BINDIR)\compile_et.exe $(SRCDIR)\heim_err.et
+ cd $(SRCDIR)
+
test:: test-binaries test-run
test-run:
diff --git a/lib/base/array.c b/lib/base/array.c
index b34f9de48800..994fa7d38e4c 100644
--- a/lib/base/array.c
+++ b/lib/base/array.c
@@ -46,7 +46,7 @@ struct heim_array_data {
heim_object_t *allocated;
};
-static void
+static void HEIM_CALLCONV
array_dealloc(heim_object_t ptr)
{
heim_array_t array = ptr;
@@ -58,7 +58,7 @@ array_dealloc(heim_object_t ptr)
struct heim_type_data array_object = {
HEIM_TID_ARRAY,
- "dict-object",
+ "array-object",
NULL,
array_dealloc,
NULL,
diff --git a/lib/base/baselocl.h b/lib/base/baselocl.h
index b24c13d4fb24..7ca6439b33b3 100644
--- a/lib/base/baselocl.h
+++ b/lib/base/baselocl.h
@@ -37,6 +37,13 @@
#include <roken.h>
+#define ISTILDE(x) (x == '~')
+#ifdef _WIN32
+# define ISPATHSEP(x) (x == '/' || x =='\\')
+#else
+# define ISPATHSEP(x) (x == '/')
+#endif
+
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
@@ -60,94 +67,6 @@
#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)
@@ -165,3 +84,15 @@ heim_base_atomic_dec(heim_base_atomic_type *x)
#define HEIMDAL_NORETURN_ATTRIBUTE
#undef HEIMDAL_PRINTF_ATTRIBUTE
#define HEIMDAL_PRINTF_ATTRIBUTE(x)
+
+struct heim_context_s {
+ heim_log_facility *log_dest;
+ heim_log_facility *warn_dest;
+ heim_log_facility *debug_dest;
+ char *time_fmt;
+ unsigned int log_utc:1;
+ unsigned int homedir_access:1;
+ struct et_list *et_list;
+ char *error_string;
+ heim_error_code error_code;
+};
diff --git a/lib/base/common_plugin.h b/lib/base/common_plugin.h
new file mode 100644
index 000000000000..d7b6bcae48d1
--- /dev/null
+++ b/lib/base/common_plugin.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2018 AuriStor, Inc.
+ *
+ * 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 HEIMDAL_BASE_COMMON_PLUGIN_H
+#define HEIMDAL_BASE_COMMON_PLUGIN_H
+
+#include <heimbase-svc.h>
+
+#ifdef _WIN32
+# ifndef HEIM_CALLCONV
+# define HEIM_CALLCONV __stdcall
+# endif
+# ifndef HEIM_LIB_CALL
+# define HEIM_LIB_CALL __stdcall
+# endif
+#else
+# ifndef HEIM_CALLCONV
+# define HEIM_CALLCONV
+# endif
+# ifndef HEIM_LIB_CALL
+# define HEIM_LIB_CALL
+# endif
+#endif
+#ifndef KRB5_CALLCONV
+# define KRB5_CALLCONV HEIM_CALLCONV
+#endif
+#ifndef KRB5_LIB_CALL
+# define KRB5_LIB_CALL HEIM_LIB_CALL
+#endif
+
+/* For krb5 plugins, this is a krb5_context */
+typedef struct heim_pcontext_s *heim_pcontext;
+
+typedef uintptr_t
+(HEIM_LIB_CALL *heim_get_instance_func_t)(const char *);
+typedef heim_get_instance_func_t krb5_get_instance_t;
+
+/*
+ * All plugin function tables extend the following structure.
+ */
+struct heim_plugin_common_ftable_desc {
+ HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(heim_pcontext);
+};
+typedef struct heim_plugin_common_ftable_desc heim_plugin_common_ftable;
+typedef struct heim_plugin_common_ftable_desc *heim_plugin_common_ftable_p;
+typedef const struct heim_plugin_common_ftable_desc *heim_plugin_common_ftable_const_p;
+typedef struct heim_plugin_common_ftable_desc * const heim_plugin_common_ftable_cp;
+
+typedef int
+(HEIM_CALLCONV heim_plugin_load_ft)(heim_pcontext context,
+ heim_get_instance_func_t *func,
+ size_t *n_ftables,
+ heim_plugin_common_ftable_cp **ftables);
+
+typedef heim_plugin_load_ft *heim_plugin_load_t;
+
+/* For source backwards-compatibility */
+typedef struct heim_plugin_common_ftable_desc krb5_plugin_common_ftable;
+typedef struct heim_plugin_common_ftable_desc *krb5_plugin_common_ftable_p;
+typedef struct heim_plugin_common_ftable_desc * const krb5_plugin_common_ftable_cp;
+typedef heim_plugin_load_ft krb5_plugin_load_ft;
+typedef heim_plugin_load_ft *krb5_plugin_load_t;
+
+/*
+ * All plugins must export a function named "<type>_plugin_load" with
+ * a signature of:
+ *
+ * int HEIM_CALLCONV
+ * <type>_plugin_load(heim_pcontext context,
+ * heim_get_instance_func_t *func,
+ * size_t *n_ftables,
+ * const heim_plugin_common_ftable *const **ftables);
+ */
+#endif /* HEIMDAL_BASE_COMMON_PLUGIN_H */
diff --git a/lib/base/config_file.c b/lib/base/config_file.c
new file mode 100644
index 000000000000..b1675ea5f141
--- /dev/null
+++ b/lib/base/config_file.c
@@ -0,0 +1,1472 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 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 <assert.h>
+#include <ctype.h>
+#include <parse_time.h>
+
+#if defined(HAVE_FRAMEWORK_COREFOUNDATION)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+/* Gaah! I want a portable funopen */
+struct fileptr {
+ heim_context context;
+ const char *s;
+ FILE *f;
+};
+
+static char *
+config_fgets(char *str, size_t len, struct fileptr *ptr)
+{
+ /* XXX this is not correct, in that they don't do the same if the
+ line is longer than len */
+ if(ptr->f != NULL)
+ return fgets(str, len, ptr->f);
+ else {
+ /* this is almost strsep_copy */
+ const char *p;
+ ssize_t l;
+ if(*ptr->s == '\0')
+ return NULL;
+ p = ptr->s + strcspn(ptr->s, "\n");
+ if(*p == '\n')
+ p++;
+ l = min(len, (size_t)(p - ptr->s));
+ if(len > 0) {
+ memcpy(str, ptr->s, l);
+ str[l] = '\0';
+ }
+ ptr->s = p;
+ return str;
+ }
+}
+
+static heim_error_code parse_section(char *p, heim_config_section **s,
+ heim_config_section **res,
+ const char **err_message);
+static heim_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ heim_config_binding **b,
+ heim_config_binding **parent,
+ const char **err_message);
+static heim_error_code parse_list(struct fileptr *f, unsigned *lineno,
+ heim_config_binding **parent,
+ const char **err_message);
+
+heim_config_section *
+heim_config_get_entry(heim_config_section **parent, const char *name, int type)
+{
+ heim_config_section **q;
+
+ for (q = parent; *q != NULL; q = &(*q)->next)
+ if (type == heim_config_list &&
+ (unsigned)type == (*q)->type &&
+ strcmp(name, (*q)->name) == 0)
+ return *q;
+ *q = calloc(1, sizeof(**q));
+ if (*q == NULL)
+ return NULL;
+ (*q)->name = strdup(name);
+ (*q)->type = type;
+ if ((*q)->name == NULL) {
+ free(*q);
+ *q = NULL;
+ return NULL;
+ }
+ return *q;
+}
+
+/*
+ * Parse a section:
+ *
+ * [section]
+ * foo = bar
+ * b = {
+ * a
+ * }
+ * ...
+ *
+ * starting at the line in `p', storing the resulting structure in
+ * `s' and hooking it into `parent'.
+ * Store the error message in `err_message'.
+ */
+
+static heim_error_code
+parse_section(char *p, heim_config_section **s, heim_config_section **parent,
+ const char **err_message)
+{
+ char *p1;
+ heim_config_section *tmp;
+
+ p1 = strchr (p + 1, ']');
+ if (p1 == NULL) {
+ *err_message = "missing ]";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ *p1 = '\0';
+ tmp = heim_config_get_entry(parent, p + 1, heim_config_list);
+ if(tmp == NULL) {
+ *err_message = "out of memory";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ *s = tmp;
+ return 0;
+}
+
+/*
+ * Parse a brace-enclosed list from `f', hooking in the structure at
+ * `parent'.
+ * Store the error message in `err_message'.
+ */
+
+static heim_error_code
+parse_list(struct fileptr *f, unsigned *lineno, heim_config_binding **parent,
+ const char **err_message)
+{
+ char buf[2048];
+ heim_error_code ret;
+ heim_config_binding *b = NULL;
+ unsigned beg_lineno = *lineno;
+
+ while(config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';' || *p == '\0')
+ continue;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '}')
+ return 0;
+ if (*p == '\0')
+ continue;
+ ret = parse_binding (f, lineno, p, &b, parent, err_message);
+ if (ret)
+ return ret;
+ }
+ *lineno = beg_lineno;
+ *err_message = "unclosed {";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+}
+
+/*
+ *
+ */
+
+static heim_error_code
+parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ heim_config_binding **b, heim_config_binding **parent,
+ const char **err_message)
+{
+ heim_config_binding *tmp;
+ char *p1, *p2;
+ heim_error_code ret = 0;
+
+ p1 = p;
+ while (*p && *p != '=' && !isspace((unsigned char)*p))
+ ++p;
+ if (*p == '\0') {
+ *err_message = "missing =";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ p2 = p;
+ while (isspace((unsigned char)*p))
+ ++p;
+ if (*p != '=') {
+ *err_message = "missing =";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ++p;
+ while(isspace((unsigned char)*p))
+ ++p;
+ *p2 = '\0';
+ if (*p == '{') {
+ tmp = heim_config_get_entry(parent, p1, heim_config_list);
+ if (tmp == NULL) {
+ *err_message = "out of memory";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ret = parse_list (f, lineno, &tmp->u.list, err_message);
+ } else {
+ tmp = heim_config_get_entry(parent, p1, heim_config_string);
+ if (tmp == NULL) {
+ *err_message = "out of memory";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ p1 = p;
+ p = p1 + strlen(p1);
+ while(p > p1 && isspace((unsigned char)*(p-1)))
+ --p;
+ *p = '\0';
+ tmp->u.string = strdup(p1);
+ }
+ *b = tmp;
+ return ret;
+}
+
+#if defined(HAVE_FRAMEWORK_COREFOUNDATION)
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+#define HAVE_CFPROPERTYLISTCREATEWITHSTREAM 1
+#endif
+
+static char *
+cfstring2cstring(CFStringRef string)
+{
+ CFIndex len;
+ char *str;
+
+ str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
+ if (str)
+ return strdup(str);
+
+ len = CFStringGetLength(string);
+ len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
+ str = malloc(len);
+ if (str == NULL)
+ return NULL;
+
+ if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) {
+ free (str);
+ return NULL;
+ }
+ return str;
+}
+
+static void
+convert_content(const void *key, const void *value, void *context)
+{
+ heim_config_section *tmp, **parent = context;
+ char *k;
+
+ if (CFGetTypeID(key) != CFStringGetTypeID())
+ return;
+
+ k = cfstring2cstring(key);
+ if (k == NULL)
+ return;
+
+ if (CFGetTypeID(value) == CFStringGetTypeID()) {
+ tmp = heim_config_get_entry(parent, k, heim_config_string);
+ tmp->u.string = cfstring2cstring(value);
+ } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
+ tmp = heim_config_get_entry(parent, k, heim_config_list);
+ CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
+ } else {
+ /* log */
+ }
+ free(k);
+}
+
+static heim_error_code
+parse_plist_config(heim_context context, const char *path, heim_config_section **parent)
+{
+ CFReadStreamRef s;
+ CFDictionaryRef d;
+ CFURLRef url;
+
+ url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), 0);
+ if (url == NULL) {
+ heim_clear_error_message(context);
+ return ENOMEM;
+ }
+
+ s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+ CFRelease(url);
+ if (s == NULL) {
+ heim_clear_error_message(context);
+ return ENOMEM;
+ }
+
+ if (!CFReadStreamOpen(s)) {
+ CFRelease(s);
+ heim_clear_error_message(context);
+ return ENOENT;
+ }
+
+#ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
+ d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
+#else
+ d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
+#endif
+ CFRelease(s);
+ if (d == NULL) {
+ heim_clear_error_message(context);
+ return ENOENT;
+ }
+
+ CFDictionaryApplyFunction(d, convert_content, parent);
+ CFRelease(d);
+
+ return 0;
+}
+
+#endif
+
+static int
+is_absolute_path(const char *path)
+{
+ /*
+ * An absolute path is one that refers to an explicit object
+ * without ambiguity.
+ */
+#ifdef WIN32
+ size_t len = strlen(path);
+
+ /* UNC path is by definition absolute */
+ if (len > 2
+ && ISPATHSEP(path[0])
+ && ISPATHSEP(path[1]))
+ return 1;
+
+ /* A drive letter path might be absolute */
+ if (len > 3
+ && isalpha((unsigned char)path[0])
+ && path[1] == ':'
+ && ISPATHSEP(path[2]))
+ return 1;
+
+ /*
+ * if no drive letter but first char is a path
+ * separator then the drive letter must be obtained
+ * from the including file.
+ */
+#else
+ /* UNIX is easy, first char '/' is absolute */
+ if (ISPATHSEP(path[0]))
+ return 1;
+#endif
+ return 0;
+}
+
+/*
+ * Parse the config file `fname', generating the structures into `res'
+ * returning error messages in `err_message'
+ */
+
+static heim_error_code
+heim_config_parse_debug(struct fileptr *f,
+ heim_config_section **res,
+ unsigned *lineno,
+ const char **err_message)
+{
+ heim_config_section *s = NULL;
+ heim_config_binding *b = NULL;
+ char buf[2048];
+ heim_error_code ret;
+
+ *lineno = 0;
+ *err_message = "";
+
+ while (config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';')
+ continue;
+ if (*p == '[') {
+ ret = parse_section(p, &s, res, err_message);
+ if (ret)
+ return ret;
+ b = NULL;
+ } else if (*p == '}') {
+ *err_message = "unmatched }";
+ return 2048;
+ } else if (strncmp(p, "include", sizeof("include") - 1) == 0 &&
+ isspace((unsigned char)p[sizeof("include") - 1])) {
+ p += sizeof("include");
+ while (isspace((unsigned char)*p))
+ p++;
+ if (!is_absolute_path(p)) {
+ heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT,
+ "Configuration include path must be "
+ "absolute");
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ret = heim_config_parse_file_multi(f->context, p, res);
+ if (ret)
+ return ret;
+ } else if (strncmp(p, "includedir", sizeof("includedir") - 1) == 0 &&
+ isspace((unsigned char)p[sizeof("includedir") - 1])) {
+ p += sizeof("includedir");
+ while (isspace((unsigned char)*p))
+ p++;
+ if (!is_absolute_path(p)) {
+ heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT,
+ "Configuration includedir path must be "
+ "absolute");
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ret = heim_config_parse_dir_multi(f->context, p, res);
+ if (ret)
+ return ret;
+ } else if(*p != '\0') {
+ if (s == NULL) {
+ *err_message = "binding before section";
+ return 2048;
+ }
+ ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int
+is_plist_file(const char *fname)
+{
+ size_t len = strlen(fname);
+ char suffix[] = ".plist";
+ if (len < sizeof(suffix))
+ return 0;
+ if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * Parse configuration files in the given directory and add the result
+ * into res. Only files whose names consist only of alphanumeric
+ * characters, hyphen, and underscore, will be parsed, though files
+ * ending in ".conf" will also be parsed.
+ *
+ * This interface can be used to parse several configuration directories
+ * into one resulting heim_config_section by calling it repeatably.
+ *
+ * @param context a Kerberos 5 context.
+ * @param dname a directory name to a Kerberos configuration file
+ * @param res the returned result, must be free with heim_free_config_files().
+ * @return Return an error code or 0, see heim_get_error_message().
+ *
+ * @ingroup heim_support
+ */
+
+heim_error_code
+heim_config_parse_dir_multi(heim_context context,
+ const char *dname,
+ heim_config_section **res)
+{
+ struct dirent *entry;
+ heim_error_code ret;
+ DIR *d;
+
+ if ((d = opendir(dname)) == NULL)
+ return errno;
+
+ while ((entry = readdir(d)) != NULL) {
+ char *p = entry->d_name;
+ char *path;
+ int is_valid = 1;
+
+ while (*p) {
+ /*
+ * Here be dragons. The call to heim_config_parse_file_multi()
+ * below expands path tokens. Because of the limitations here
+ * on file naming, we can't have path tokens in the file name,
+ * so we're safe. Anyone changing this if condition here should
+ * be aware.
+ */
+ if (!isalnum((unsigned char)*p) && *p != '_' && *p != '-' &&
+ strcmp(p, ".conf") != 0) {
+ is_valid = 0;
+ break;
+ }
+ p++;
+ }
+ if (!is_valid)
+ continue;
+
+ if (asprintf(&path, "%s/%s", dname, entry->d_name) == -1 ||
+ path == NULL) {
+ (void) closedir(d);
+ return heim_enomem(context);
+ }
+ ret = heim_config_parse_file_multi(context, path, res);
+ free(path);
+ if (ret == ENOMEM) {
+ (void) closedir(d);
+ return ENOMEM;
+ }
+ /* Ignore malformed config files so we don't lock out admins, etc... */
+ }
+ (void) closedir(d);
+ return 0;
+}
+
+static int
+is_devnull(struct stat *st)
+{
+#ifdef WIN32
+ return 0;
+#else
+ struct stat devnullst;
+
+ if (stat("/dev/null", &devnullst) == -1)
+ return 0;
+ return st->st_dev == devnullst.st_dev && st->st_ino == devnullst.st_ino;
+#endif
+}
+
+HEIMDAL_THREAD_LOCAL int config_include_depth = 0;
+
+/**
+ * Parse a configuration file and add the result into res. This
+ * interface can be used to parse several configuration files into one
+ * resulting heim_config_section by calling it repeatably.
+ *
+ * @param context a Kerberos 5 context.
+ * @param fname a file name to a Kerberos configuration file
+ * @param res the returned result, must be free with heim_free_config_files().
+ * @return Return an error code or 0, see heim_get_error_message().
+ *
+ * @ingroup heim_support
+ */
+
+heim_error_code
+heim_config_parse_file_multi(heim_context context,
+ const char *fname,
+ heim_config_section **res)
+{
+ const char *str;
+ char *newfname = NULL;
+ unsigned lineno = 0;
+ heim_error_code ret = 0;
+ struct fileptr f;
+ struct stat st;
+
+ if (config_include_depth > 5) {
+ heim_warnx(context, "Maximum config file include depth reached; "
+ "not including %s", fname);
+ return 0;
+ }
+ config_include_depth++;
+
+ /**
+ * If the fname starts with "~/" parse configuration file in the
+ * current users home directory. The behavior can be disabled and
+ * enabled by calling heim_set_home_dir_access().
+ */
+ if (ISTILDE(fname[0]) && ISPATHSEP(fname[1])) {
+ if (!heim_context_get_homedir_access(context)) {
+ heim_set_error_message(context, EPERM,
+ "Access to home directory not allowed");
+ ret = EPERM;
+ goto out;
+ }
+ if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
+ newfname == NULL) {
+ ret = heim_enomem(context);
+ goto out;
+ }
+ fname = newfname;
+ }
+
+ if (is_plist_file(fname)) {
+#if defined(HAVE_FRAMEWORK_COREFOUNDATION)
+ ret = parse_plist_config(context, fname, res);
+ if (ret) {
+ heim_set_error_message(context, ret,
+ "Failed to parse plist %s", fname);
+ goto out;
+ }
+#else
+ heim_set_error_message(context, ENOENT,
+ "no support for plist configuration files");
+ ret = ENOENT;
+ goto out;
+#endif
+ } else {
+ char *exp_fname = NULL;
+
+ /*
+ * Note that heim_config_parse_dir_multi() doesn't want tokens
+ * expanded here, but it happens to limit the names of files to
+ * include such that there can be no tokens to expand. Don't
+ * add token expansion for tokens using _, say.
+ */
+ ret = heim_expand_path_tokens(context, fname, 1, &exp_fname, NULL);
+ if (ret)
+ goto out;
+ free(newfname);
+ fname = newfname = exp_fname;
+
+ f.context = context;
+ f.f = fopen(fname, "r");
+ f.s = NULL;
+ if (f.f == NULL || fstat(fileno(f.f), &st) == -1) {
+ if (f.f != NULL)
+ (void) fclose(f.f);
+ ret = errno;
+ heim_set_error_message(context, ret, "open or stat %s: %s",
+ fname, strerror(ret));
+ goto out;
+ }
+
+ if (!S_ISREG(st.st_mode) && !is_devnull(&st)) {
+ (void) fclose(f.f);
+ heim_set_error_message(context, EISDIR, "not a regular file %s: %s",
+ fname, strerror(EISDIR));
+ ret = EISDIR;
+ goto out;
+ }
+
+ ret = heim_config_parse_debug(&f, res, &lineno, &str);
+ fclose(f.f);
+ if (ret) {
+ if (ret != HEIM_ERR_CONFIG_BADFORMAT)
+ ret = HEIM_ERR_CONFIG_BADFORMAT;
+ heim_set_error_message(context, ret, "%s:%u: %s",
+ fname, lineno, str);
+ goto out;
+ }
+ }
+
+ out:
+ config_include_depth--;
+ if (ret == HEIM_ERR_CONFIG_BADFORMAT || (ret && config_include_depth > 0)) {
+ heim_warn(context, ret, "Ignoring");
+ if (config_include_depth > 0)
+ ret = 0;
+ }
+ free(newfname);
+ return ret;
+}
+
+heim_error_code
+heim_config_parse_file(heim_context context,
+ const char *fname,
+ heim_config_section **res)
+{
+ *res = NULL;
+ return heim_config_parse_file_multi(context, fname, res);
+}
+
+static void
+free_binding(heim_context context, heim_config_binding *b)
+{
+ heim_config_binding *next_b;
+
+ while (b) {
+ free (b->name);
+ assert(b->type == heim_config_string || b->type == heim_config_list);
+ if (b->type == heim_config_string)
+ free (b->u.string);
+ else
+ free_binding (context, b->u.list);
+ next_b = b->next;
+ free (b);
+ b = next_b;
+ }
+}
+
+/**
+ * Free configuration file section, the result of
+ * heim_config_parse_file() and heim_config_parse_file_multi().
+ *
+ * @param context A Kerberos 5 context
+ * @param s the configuration section to free
+ *
+ * @return returns 0 on successes, otherwise an error code, see
+ * heim_get_error_message()
+ *
+ * @ingroup heim_support
+ */
+
+heim_error_code
+heim_config_file_free(heim_context context, heim_config_section *s)
+{
+ free_binding (context, s);
+ return 0;
+}
+
+#ifndef HEIMDAL_SMALLER
+
+heim_error_code
+heim_config_copy(heim_context context,
+ heim_config_section *c,
+ heim_config_section **head)
+{
+ heim_config_binding *d, *previous = NULL;
+
+ *head = NULL;
+
+ while (c) {
+ d = calloc(1, sizeof(*d));
+
+ if (*head == NULL)
+ *head = d;
+
+ d->name = strdup(c->name);
+ d->type = c->type;
+ assert(d->type == heim_config_string || d->type == heim_config_list);
+ if (d->type == heim_config_string)
+ d->u.string = strdup(c->u.string);
+ else
+ heim_config_copy (context, c->u.list, &d->u.list);
+ if (previous)
+ previous->next = d;
+
+ previous = d;
+ c = c->next;
+ }
+ return 0;
+}
+
+#endif /* HEIMDAL_SMALLER */
+
+const void *
+heim_config_get_next(heim_context context,
+ const heim_config_section *c,
+ const heim_config_binding **pointer,
+ int type,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = heim_config_vget_next(context, c, pointer, type, args);
+ va_end(args);
+ return ret;
+}
+
+static const void *
+vget_next(heim_context context,
+ const heim_config_binding *b,
+ const heim_config_binding **pointer,
+ int type,
+ const char *name,
+ va_list args)
+{
+ const char *p = va_arg(args, const char *);
+
+ while (b != NULL) {
+ if (strcmp(b->name, name) == 0) {
+ if (b->type == (unsigned)type && p == NULL) {
+ *pointer = b;
+ return b->u.generic;
+ } else if (b->type == heim_config_list && p != NULL) {
+ return vget_next(context, b->u.list, pointer, type, p, args);
+ }
+ }
+ b = b->next;
+ }
+ return NULL;
+}
+
+const void *
+heim_config_vget_next(heim_context context,
+ const heim_config_section *c,
+ const heim_config_binding **pointer,
+ int type,
+ va_list args)
+{
+ const heim_config_binding *b;
+ const char *p;
+
+ if (c == NULL)
+ return NULL;
+
+ if (*pointer == NULL) {
+ /* first time here, walk down the tree looking for the right
+ section */
+ p = va_arg(args, const char *);
+ if (p == NULL)
+ return NULL;
+ return vget_next(context, c, pointer, type, p, args);
+ }
+
+ /* we were called again, so just look for more entries with the
+ same name and type */
+ for (b = (*pointer)->next; b != NULL; b = b->next) {
+ if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) {
+ *pointer = b;
+ return b->u.generic;
+ }
+ }
+ return NULL;
+}
+
+const void *
+heim_config_get(heim_context context,
+ const heim_config_section *c,
+ int type,
+ ...)
+{
+ const void *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = heim_config_vget(context, c, type, args);
+ va_end(args);
+ return ret;
+}
+
+
+const void *
+heim_config_vget(heim_context context,
+ const heim_config_section *c,
+ int type,
+ va_list args)
+{
+ const heim_config_binding *foo = NULL;
+
+ return heim_config_vget_next(context, c, &foo, type, args);
+}
+
+/**
+ * Get a list of configuration binding list for more processing
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return NULL if configuration list is not found, a list otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const heim_config_binding *
+heim_config_get_list(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ const heim_config_binding *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = heim_config_vget_list(context, c, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Get a list of configuration binding list for more processing
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return NULL if configuration list is not found, a list otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const heim_config_binding *
+heim_config_vget_list(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget(context, c, heim_config_list, args);
+}
+
+/**
+ * Returns a "const char *" to a string in the configuration database.
+ * The string may not be valid after a reload of the configuration
+ * database so a caller should make a local copy if it needs to keep
+ * the string.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return NULL if configuration string not found, a string otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_get_string(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = heim_config_vget_string(context, c, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Like heim_config_get_string(), but uses a va_list instead of ...
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return NULL if configuration string not found, a string otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_vget_string(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget(context, c, heim_config_string, args);
+}
+
+/**
+ * Like heim_config_vget_string(), but instead of returning NULL,
+ * instead return a default value.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return a configuration string
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_vget_string_default(heim_context context,
+ const heim_config_section *c,
+ const char *def_value,
+ va_list args)
+{
+ const char *ret;
+
+ ret = heim_config_vget_string(context, c, args);
+ if (ret == NULL)
+ ret = def_value;
+ return ret;
+}
+
+/**
+ * Like heim_config_get_string(), but instead of returning NULL,
+ * instead return a default value.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return a configuration string
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_get_string_default(heim_context context,
+ const heim_config_section *c,
+ const char *def_value,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, def_value);
+ ret = heim_config_vget_string_default (context, c, def_value, args);
+ va_end(args);
+ return ret;
+}
+
+static char *
+next_component_string(char * begin, const char * delims, char **state)
+{
+ char * end;
+
+ if (begin == NULL)
+ begin = *state;
+
+ if (*begin == '\0')
+ return NULL;
+
+ end = begin;
+ while (*end == '"') {
+ char * t = strchr(end + 1, '"');
+
+ if (t)
+ end = ++t;
+ else
+ end += strlen(end);
+ }
+
+ if (*end != '\0') {
+ size_t pos;
+
+ pos = strcspn(end, delims);
+ end = end + pos;
+ }
+
+ if (*end != '\0') {
+ *end = '\0';
+ *state = end + 1;
+ if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
+ begin++; *(end - 1) = '\0';
+ }
+ return begin;
+ }
+
+ *state = end;
+ if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
+ begin++; *(end - 1) = '\0';
+ }
+ return begin;
+}
+
+/**
+ * Get a list of configuration strings, free the result with
+ * heim_config_free_strings().
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+char **
+heim_config_vget_strings(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ char **strings = NULL;
+ size_t nstr = 0;
+ const heim_config_binding *b = NULL;
+ const char *p;
+
+ while((p = heim_config_vget_next(context, c, &b,
+ heim_config_string, args))) {
+ char *tmp = strdup(p);
+ char *pos = NULL;
+ char *s;
+ if(tmp == NULL)
+ goto cleanup;
+ s = next_component_string(tmp, " \t", &pos);
+ while(s){
+ char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp2 == NULL) {
+ free(tmp);
+ goto cleanup;
+ }
+ strings = tmp2;
+ strings[nstr] = strdup(s);
+ nstr++;
+ if(strings[nstr-1] == NULL) {
+ free(tmp);
+ goto cleanup;
+ }
+ s = next_component_string(NULL, " \t", &pos);
+ }
+ free(tmp);
+ }
+ if(nstr){
+ char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp == NULL)
+ goto cleanup;
+ strings = tmp;
+ strings[nstr] = NULL;
+ }
+ return strings;
+cleanup:
+ while(nstr--)
+ free(strings[nstr]);
+ free(strings);
+ return NULL;
+
+}
+
+/**
+ * Get a list of configuration strings, free the result with
+ * heim_config_free_strings().
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+char **
+heim_config_get_strings(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ char **ret;
+ va_start(ap, c);
+ ret = heim_config_vget_strings(context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Free the resulting strings from heim_config-get_strings() and
+ * heim_config_vget_strings().
+ *
+ * @param strings strings to free
+ *
+ * @ingroup heim_support
+ */
+
+void
+heim_config_free_strings(char **strings)
+{
+ char **s = strings;
+
+ while (s && *s) {
+ free(*s);
+ s++;
+ }
+ free(strings);
+}
+
+/**
+ * Like heim_config_get_bool_default() but with a va_list list of
+ * configuration selection.
+ *
+ * Configuration value to a boolean value, where yes/true and any
+ * non-zero number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_vget_bool_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ str = heim_config_vget_string(context, c, args);
+ if (str == NULL)
+ return def_value;
+ return !!(strcasecmp(str, "yes") == 0 ||
+ strcasecmp(str, "true") == 0 ||
+ atoi(str));
+}
+
+/**
+ * heim_config_get_bool() will convert the configuration
+ * option value to a boolean value, where yes/true and any non-zero
+ * number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_vget_bool(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget_bool_default(context, c, 0, args);
+}
+
+/**
+ * heim_config_get_bool_default() will convert the configuration
+ * option value to a boolean value, where yes/true and any non-zero
+ * number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_get_bool_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, def_value);
+ ret = heim_config_vget_bool_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Like heim_config_get_bool() but with a va_list list of
+ * configuration selection.
+ *
+ * Configuration value to a boolean value, where yes/true and any
+ * non-zero number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_get_bool(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = heim_config_vget_bool (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Get the time from the configuration file using a relative time.
+ *
+ * Like heim_config_get_time_default() but with a va_list list of
+ * configuration selection.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return parsed the time (or def_value on parse error)
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_vget_time_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ time_t t = -1;
+
+ if ((str = heim_config_vget_string(context, c, args)))
+ t = parse_time(str, "s");
+ return t != -1 ? t : def_value;
+}
+
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return parsed the time or -1 on error
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_vget_time(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget_time_default(context, c, -1, args);
+}
+
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return parsed the time (or def_value on parse error)
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_get_time_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ time_t ret;
+
+ va_start(ap, def_value);
+ ret = heim_config_vget_time_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return parsed the time or -1 on error
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_get_time(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = heim_config_vget_time(context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+int
+heim_config_vget_int_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ str = heim_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ else {
+ char *endptr;
+ long l;
+ l = strtol(str, &endptr, 0);
+ if (endptr == str)
+ return def_value;
+ else
+ return l;
+ }
+}
+
+int
+heim_config_vget_int(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget_int_default(context, c, -1, args);
+}
+
+int
+heim_config_get_int_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, def_value);
+ ret = heim_config_vget_int_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+int
+heim_config_get_int(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = heim_config_vget_int (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+#ifndef HEIMDAL_SMALLER
+heim_error_code
+heim_config_parse_string_multi(heim_context context,
+ const char *string,
+ heim_config_section **res)
+{
+ const char *str;
+ unsigned lineno = 0;
+ heim_error_code ret;
+ struct fileptr f;
+
+ f.context = context;
+ f.f = NULL;
+ f.s = string;
+
+ ret = heim_config_parse_debug(&f, res, &lineno, &str);
+ if (ret) {
+ if (ret != HEIM_ERR_CONFIG_BADFORMAT) {
+ ret = HEIM_ERR_CONFIG_BADFORMAT;
+ heim_set_error_message(context, ret, "%s:%u: %s",
+ "<constant>", lineno, str);
+ }
+ return ret;
+ }
+ return 0;
+}
+#endif
diff --git a/lib/base/config_reg.c b/lib/base/config_reg.c
new file mode 100644
index 000000000000..cb24e5043d64
--- /dev/null
+++ b/lib/base/config_reg.c
@@ -0,0 +1,658 @@
+/***********************************************************************
+ * 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.
+ *
+ **********************************************************************/
+
+#include "baselocl.h"
+
+#ifndef _WIN32
+#error config_reg.c is only for Windows
+#endif
+
+#include <shlwapi.h>
+
+#ifndef MAX_DWORD
+#define MAX_DWORD 0xFFFFFFFF
+#endif
+
+/**
+ * Store a string as a registry value of the specified type
+ *
+ * The following registry types are handled:
+ *
+ * - REG_DWORD: The string is converted to a number.
+ *
+ * - REG_SZ: The string is stored as is.
+ *
+ * - REG_EXPAND_SZ: The string is stored as is.
+ *
+ * - REG_MULTI_SZ:
+ *
+ * . If a separator is specified, the input string is broken
+ * up into multiple strings and stored as a multi-sz.
+ *
+ * . If no separator is provided, the input string is stored
+ * as a multi-sz.
+ *
+ * - REG_NONE:
+ *
+ * . If the string is all numeric, it will be stored as a
+ * REG_DWORD.
+ *
+ * . Otherwise, the string is stored as a REG_SZ.
+ *
+ * Other types are rejected.
+ *
+ * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
+ * otherwise a buffer overrun will occur.
+ *
+ * @param [in]valuename Name of the registry value to be modified or created
+ * @param [in]type Type of the value. REG_NONE if unknown
+ * @param [in]data The input string to be stored in the registry.
+ * @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown.
+ * @param [in]separator Separator character for parsing strings.
+ *
+ * @retval 0 if success or non-zero on error.
+ * If non-zero is returned, an error message has been set using
+ * heim_set_error_message().
+ *
+ */
+int
+heim_store_string_to_reg_value(heim_context context,
+ HKEY key, const char *valuename,
+ DWORD type, const char *data, DWORD cb_data,
+ const char *separator)
+{
+ LONG rcode;
+ int dwData;
+ BYTE static_buffer[16384];
+
+ if (data == NULL)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "'data' must not be NULL");
+ return -1;
+ }
+
+ if (cb_data == MAX_DWORD)
+ {
+ cb_data = (DWORD)strlen(data) + 1;
+ }
+ else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
+ cb_data >= sizeof(static_buffer))
+ {
+ if (context)
+ heim_set_error_message(context, 0, "cb_data too big");
+ return -1;
+ }
+ else if (data[cb_data-1] != '\0')
+ {
+ memcpy(static_buffer, data, cb_data);
+ static_buffer[cb_data++] = '\0';
+ if (type == REG_MULTI_SZ)
+ static_buffer[cb_data++] = '\0';
+ data = static_buffer;
+ }
+
+ if (type == REG_NONE)
+ {
+ /*
+ * If input is all numeric, convert to DWORD and save as REG_DWORD.
+ * Otherwise, store as REG_SZ.
+ */
+ if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
+ {
+ type = REG_DWORD;
+ } else {
+ type = REG_SZ;
+ }
+ }
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
+ if (rcode)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ break;
+ case REG_MULTI_SZ:
+ if (separator && *separator)
+ {
+ char *cp;
+
+ if (data != static_buffer)
+ static_buffer[cb_data++] = '\0';
+
+ for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
+ {
+ if (*cp == *separator)
+ *cp = '\0';
+ }
+
+ rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
+ if (rcode)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ }
+ break;
+ case REG_DWORD:
+ if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when parsing %s as number gle 0x%x",
+ data,
+ GetLastError());
+ }
+
+ rcode = RegSetValueEx(key, valuename, 0, type, (BYTE *)&dwData, sizeof(DWORD));
+ if (rcode)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Parse a registry value as a string
+ *
+ * @see heim_parse_reg_value_as_multi_string()
+ */
+char *
+heim_parse_reg_value_as_string(heim_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cb_data)
+{
+ return heim_parse_reg_value_as_multi_string(context, key, valuename,
+ type, cb_data, " ");
+}
+
+/**
+ * Parse a registry value as a multi string
+ *
+ * The following registry value types are handled:
+ *
+ * - REG_DWORD: The decimal string representation is used as the
+ * value.
+ *
+ * - REG_SZ: The string is used as-is.
+ *
+ * - REG_EXPAND_SZ: Environment variables in the string are expanded
+ * and the result is used as the value.
+ *
+ * - REG_MULTI_SZ: The list of strings is concatenated using the
+ * separator. No quoting is performed.
+ *
+ * Any other value type is rejected.
+ *
+ * @param [in]valuename Name of the registry value to be queried
+ * @param [in]type Type of the value. REG_NONE if unknown
+ * @param [in]cbdata Size of value. 0 if unknown.
+ * @param [in]separator Separator character for concatenating strings.
+ *
+ * @a type and @a cbdata are only considered valid if both are
+ * specified.
+ *
+ * @retval The registry value string, or NULL if there was an error.
+ * If NULL is returned, an error message has been set using
+ * heim_set_error_message().
+ */
+char *
+heim_parse_reg_value_as_multi_string(heim_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cb_data, char *separator)
+{
+ LONG rcode = ERROR_MORE_DATA;
+
+ BYTE static_buffer[16384];
+ BYTE *pbuffer = &static_buffer[0];
+ DWORD cb_alloc = sizeof(static_buffer);
+ char *ret_string = NULL;
+
+ /* If we know a type and cb_data from a previous call to
+ * RegEnumValue(), we use it. Otherwise we use the
+ * static_buffer[] and query directly. We do this to minimize the
+ * number of queries. */
+
+ if (type == REG_NONE || cb_data == 0) {
+
+ pbuffer = &static_buffer[0];
+ cb_alloc = cb_data = sizeof(static_buffer);
+ rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
+
+ if (rcode == ERROR_SUCCESS &&
+
+ ((type != REG_SZ &&
+ type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
+
+ (type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
+ goto have_data;
+
+ if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
+ return NULL;
+ }
+
+ /* Either we don't have the data or we aren't sure of the size
+ * (due to potentially missing terminating NULs). */
+
+ switch (type) {
+ case REG_DWORD:
+ if (cb_data != sizeof(DWORD)) {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected size while reading registry value %s",
+ valuename);
+ return NULL;
+ }
+ break;
+
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+
+ if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
+ goto have_data;
+
+ cb_data += sizeof(char); /* Accout for potential missing NUL
+ * terminator. */
+ break;
+
+ case REG_MULTI_SZ:
+
+ if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
+ (cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
+ goto have_data;
+
+ cb_data += sizeof(char) * 2; /* Potential missing double NUL
+ * terminator. */
+ break;
+
+ default:
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected type while reading registry value %s",
+ valuename);
+ return NULL;
+ }
+
+ if (cb_data <= sizeof(static_buffer))
+ pbuffer = &static_buffer[0];
+ else {
+ pbuffer = malloc(cb_data);
+ if (pbuffer == NULL)
+ return NULL;
+ }
+
+ cb_alloc = cb_data;
+ rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
+
+ if (rcode != ERROR_SUCCESS) {
+
+ /* This can potentially be from a race condition. I.e. some
+ * other process or thread went and modified the registry
+ * value between the time we queried its size and queried for
+ * its value. Ideally we would retry the query in a loop. */
+
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error while reading registry value %s",
+ valuename);
+ goto done;
+ }
+
+ if (cb_data > cb_alloc || cb_data == 0) {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected size while reading registry value %s",
+ valuename);
+ goto done;
+ }
+
+have_data:
+ switch (type) {
+ case REG_DWORD:
+ asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
+ break;
+
+ case REG_SZ:
+ {
+ char * str = (char *) pbuffer;
+
+ if (str[cb_data - 1] != '\0') {
+ if (cb_data < cb_alloc)
+ str[cb_data] = '\0';
+ else
+ break;
+ }
+
+ if (pbuffer != static_buffer) {
+ ret_string = (char *) pbuffer;
+ pbuffer = NULL;
+ } else {
+ ret_string = strdup((char *) pbuffer);
+ }
+ }
+ break;
+
+ case REG_EXPAND_SZ:
+ {
+ char *str = (char *) pbuffer;
+ char expsz[32768]; /* Size of output buffer for
+ * ExpandEnvironmentStrings() is
+ * limited to 32K. */
+
+ if (str[cb_data - 1] != '\0') {
+ if (cb_data < cb_alloc)
+ str[cb_data] = '\0';
+ else
+ break;
+ }
+
+ if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
+ ret_string = strdup(expsz);
+ } else {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Overflow while expanding environment strings "
+ "for registry value %s", valuename);
+ }
+ }
+ break;
+
+ case REG_MULTI_SZ:
+ {
+ char * str = (char *) pbuffer;
+ char * iter;
+
+ str[cb_alloc - 1] = '\0';
+ str[cb_alloc - 2] = '\0';
+
+ for (iter = str; *iter;) {
+ size_t len = strlen(iter);
+
+ iter += len;
+ if (iter[1] != '\0')
+ *iter++ = *separator;
+ else
+ break;
+ }
+
+ if (pbuffer != static_buffer) {
+ ret_string = str;
+ pbuffer = NULL;
+ } else {
+ ret_string = strdup(str);
+ }
+ }
+ break;
+
+ default:
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected type while reading registry value %s",
+ valuename);
+ }
+
+done:
+ if (pbuffer != static_buffer && pbuffer != NULL)
+ free(pbuffer);
+
+ return ret_string;
+}
+
+/**
+ * Parse a registry value as a configuration value
+ *
+ * @see parse_reg_value_as_string()
+ */
+static heim_error_code
+parse_reg_value(heim_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cbdata, heim_config_section ** parent)
+{
+ char *reg_string = NULL;
+ heim_config_section *value;
+ heim_error_code code = 0;
+
+ reg_string = heim_parse_reg_value_as_string(context, key, valuename, type, cbdata);
+
+ if (reg_string == NULL)
+ return HEIM_ERR_CONFIG_BADFORMAT;
+
+ value = heim_config_get_entry(parent, valuename, heim_config_string);
+ if (value == NULL) {
+ code = ENOMEM;
+ goto done;
+ }
+
+ if (value->u.string != NULL)
+ free(value->u.string);
+
+ value->u.string = reg_string;
+ reg_string = NULL;
+
+done:
+ if (reg_string != NULL)
+ free(reg_string);
+
+ return code;
+}
+
+static heim_error_code
+parse_reg_values(heim_context context,
+ HKEY key,
+ heim_config_section ** parent)
+{
+ DWORD index;
+ LONG rcode;
+
+ for (index = 0; ; index ++) {
+ char name[16385];
+ DWORD cch = sizeof(name)/sizeof(name[0]);
+ DWORD type;
+ DWORD cbdata = 0;
+ heim_error_code code;
+
+ rcode = RegEnumValue(key, index, name, &cch, NULL,
+ &type, NULL, &cbdata);
+ if (rcode != ERROR_SUCCESS)
+ break;
+
+ if (cbdata == 0)
+ continue;
+
+ code = parse_reg_value(context, key, name, type, cbdata, parent);
+ if (code != 0)
+ return code;
+ }
+
+ return 0;
+}
+
+static heim_error_code
+parse_reg_subkeys(heim_context context,
+ HKEY key,
+ heim_config_section ** parent)
+{
+ DWORD index;
+ LONG rcode;
+
+ for (index = 0; ; index ++) {
+ HKEY subkey = NULL;
+ char name[256];
+ DWORD cch = sizeof(name)/sizeof(name[0]);
+ heim_config_section *section = NULL;
+ heim_error_code code;
+
+ rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL);
+ if (rcode != ERROR_SUCCESS)
+ break;
+
+ rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey);
+ if (rcode != ERROR_SUCCESS)
+ continue;
+
+ section = heim_config_get_entry(parent, name, heim_config_list);
+ if (section == NULL) {
+ RegCloseKey(subkey);
+ return ENOMEM;
+ }
+
+ code = parse_reg_values(context, subkey, &section->u.list);
+ if (code) {
+ RegCloseKey(subkey);
+ return code;
+ }
+
+ code = parse_reg_subkeys(context, subkey, &section->u.list);
+ if (code) {
+ RegCloseKey(subkey);
+ return code;
+ }
+
+ RegCloseKey(subkey);
+ }
+
+ return 0;
+}
+
+static heim_error_code
+parse_reg_root(heim_context context,
+ HKEY key,
+ heim_config_section ** parent)
+{
+ heim_config_section *libdefaults = NULL;
+ heim_error_code code = 0;
+
+ libdefaults = heim_config_get_entry(parent, "libdefaults", heim_config_list);
+ if (libdefaults == NULL)
+ return heim_enomem(context);
+
+ code = parse_reg_values(context, key, &libdefaults->u.list);
+ if (code)
+ return code;
+
+ return parse_reg_subkeys(context, key, parent);
+}
+
+static heim_error_code
+load_config_from_regpath(heim_context context,
+ HKEY hk_root,
+ const char* key_path,
+ heim_config_section ** res)
+{
+ HKEY key = NULL;
+ LONG rcode;
+ heim_error_code code = 0;
+
+ rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
+ if (rcode == ERROR_SUCCESS) {
+ code = parse_reg_root(context, key, res);
+ RegCloseKey(key);
+ key = NULL;
+ }
+
+ return code;
+}
+
+/**
+ * Load configuration from registry
+ *
+ * The registry keys 'HKCU\Software\Heimdal' and
+ * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each
+ * registry key corresponds to a configuration section (or bound list)
+ * and each value in a registry key is treated as a bound value. The
+ * set of values that are directly under the Heimdal key are treated
+ * as if they were defined in the [libdefaults] section.
+ *
+ * @see parse_reg_value() for details about how each type of value is handled.
+ */
+heim_error_code
+heim_load_config_from_registry(heim_context context,
+ const char *path0,
+ const char *path1,
+ heim_config_section **res)
+{
+ heim_error_code code;
+
+ if (!path0 && !path1)
+ return EINVAL;
+
+ if (path0) {
+ code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
+ path0, res);
+ if (code)
+ return code;
+ }
+
+ if (path1) {
+ code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
+ path1, res);
+ if (code)
+ return code;
+ }
+
+ if (path0) {
+ code = load_config_from_regpath(context, HKEY_CURRENT_USER,
+ path0, res);
+ if (code)
+ return code;
+ }
+
+ if (path0) {
+ code = load_config_from_regpath(context, HKEY_CURRENT_USER,
+ path1, res);
+ if (code)
+ return code;
+ }
+ return 0;
+}
diff --git a/lib/base/context.c b/lib/base/context.c
new file mode 100644
index 000000000000..f22ce9459f44
--- /dev/null
+++ b/lib/base/context.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2020 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"
+
+#undef __attribute__
+#define __attribute__(X)
+
+heim_context
+heim_context_init(void)
+{
+ heim_context context;
+
+ if ((context = calloc(1, sizeof(*context))) == NULL)
+ return NULL;
+
+ context->homedir_access = !issuid();
+ context->log_utc = 1;
+ context->error_string = NULL;
+ context->debug_dest = NULL;
+ context->warn_dest = NULL;
+ context->log_dest = NULL;
+ context->time_fmt = NULL;
+ context->et_list = NULL;
+ return context;
+}
+
+void
+heim_context_free(heim_context *contextp)
+{
+ heim_context context = *contextp;
+
+ *contextp = NULL;
+ if (!context)
+ return;
+ heim_closelog(context, context->debug_dest);
+ heim_closelog(context, context->warn_dest);
+ heim_closelog(context, context->log_dest);
+ free_error_table(context->et_list);
+ free(context->time_fmt);
+ free(context->error_string);
+ free(context);
+}
+
+heim_error_code
+heim_add_et_list(heim_context context, void (*func)(struct et_list **))
+{
+ (*func)(&context->et_list);
+ return 0;
+}
+
+heim_error_code
+heim_context_set_time_fmt(heim_context context, const char *fmt)
+{
+ char *s;
+
+ if (fmt == NULL) {
+ free(context->time_fmt);
+ return 0;
+ }
+ if ((s = strdup(fmt)) == NULL)
+ return heim_enomem(context);
+ free(context->time_fmt);
+ context->time_fmt = s;
+ return 0;
+}
+
+const char *
+heim_context_get_time_fmt(heim_context context)
+{
+ return context->time_fmt ? context->time_fmt : "%Y-%m-%dT%H:%M:%S";
+}
+
+unsigned int
+heim_context_set_log_utc(heim_context context, unsigned int log_utc)
+{
+ unsigned int old = context->log_utc;
+
+ context->log_utc = log_utc ? 1 : 0;
+ return old;
+}
+
+int
+heim_context_get_log_utc(heim_context context)
+{
+ return context->log_utc;
+}
+
+unsigned int
+heim_context_set_homedir_access(heim_context context, unsigned int homedir_access)
+{
+ unsigned int old = context->homedir_access;
+
+ context->homedir_access = homedir_access ? 1 : 0;
+ return old;
+}
+
+unsigned int
+heim_context_get_homedir_access(heim_context context)
+{
+ return context->homedir_access;
+}
+
+heim_error_code
+heim_enomem(heim_context context)
+{
+ heim_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+}
+
+heim_log_facility *
+heim_get_log_dest(heim_context context)
+{
+ return context->log_dest;
+}
+
+heim_log_facility *
+heim_get_warn_dest(heim_context context)
+{
+ return context->warn_dest;
+}
+
+heim_log_facility *
+heim_get_debug_dest(heim_context context)
+{
+ return context->debug_dest;
+}
+
+heim_error_code
+heim_set_log_dest(heim_context context, heim_log_facility *fac)
+{
+ context->log_dest = heim_log_ref(fac);
+ return 0;
+}
+
+heim_error_code
+heim_set_warn_dest(heim_context context, heim_log_facility *fac)
+{
+ context->warn_dest = fac;
+ return 0;
+}
+
+heim_error_code
+heim_set_debug_dest(heim_context context, heim_log_facility *fac)
+{
+ context->debug_dest = fac;
+ return 0;
+}
+
+#ifndef PATH_SEP
+# define PATH_SEP ":"
+#endif
+
+static heim_error_code
+add_file(char ***pfilenames, int *len, char *file)
+{
+ char **pp = *pfilenames;
+ int i;
+
+ for(i = 0; i < *len; i++) {
+ if(strcmp(pp[i], file) == 0) {
+ free(file);
+ return 0;
+ }
+ }
+
+ pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp));
+ if (pp == NULL) {
+ free(file);
+ return ENOMEM;
+ }
+
+ pp[*len] = file;
+ pp[*len + 1] = NULL;
+ *pfilenames = pp;
+ *len += 1;
+ return 0;
+}
+
+#ifdef WIN32
+static char *
+get_default_config_config_files_from_registry(const char *envvar)
+{
+ static const char *KeyName = "Software\\Heimdal"; /* XXX #define this */
+ const char *ValueName;
+ char *config_file = NULL;
+ LONG rcode;
+ HKEY key;
+
+ if (stricmp(envvar, "KRB5_CONFIG") == 0)
+ ValueName = "config";
+ else
+ ValueName = envvar;
+
+ rcode = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName, 0, KEY_READ, &key);
+ if (rcode == ERROR_SUCCESS) {
+ config_file = heim_parse_reg_value_as_multi_string(NULL, key, ValueName,
+ REG_NONE, 0, PATH_SEP);
+ RegCloseKey(key);
+ }
+
+ if (config_file)
+ return config_file;
+
+ rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &key);
+ if (rcode == ERROR_SUCCESS) {
+ config_file = heim_parse_reg_value_as_multi_string(NULL, key, ValueName,
+ REG_NONE, 0, PATH_SEP);
+ RegCloseKey(key);
+ }
+
+ return config_file;
+}
+#endif
+
+heim_error_code
+heim_prepend_config_files(const char *filelist,
+ char **pq,
+ char ***ret_pp)
+{
+ heim_error_code ret;
+ const char *p, *q;
+ char **pp;
+ int len;
+ char *fn;
+
+ pp = NULL;
+
+ len = 0;
+ p = filelist;
+ while(1) {
+ ssize_t l;
+ q = p;
+ l = strsep_copy(&q, PATH_SEP, NULL, 0);
+ if(l == -1)
+ break;
+ fn = malloc(l + 1);
+ if(fn == NULL) {
+ heim_free_config_files(pp);
+ return ENOMEM;
+ }
+ (void) strsep_copy(&p, PATH_SEP, fn, l + 1);
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ heim_free_config_files(pp);
+ return ret;
+ }
+ }
+
+ if (pq != NULL) {
+ int i;
+
+ for (i = 0; pq[i] != NULL; i++) {
+ fn = strdup(pq[i]);
+ if (fn == NULL) {
+ heim_free_config_files(pp);
+ return ENOMEM;
+ }
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ heim_free_config_files(pp);
+ return ret;
+ }
+ }
+ }
+
+ *ret_pp = pp;
+ return 0;
+}
+
+heim_error_code
+heim_prepend_config_files_default(const char *prepend,
+ const char *def,
+ const char *envvar,
+ char ***pfilenames)
+{
+ heim_error_code ret;
+ char **defpp, **pp = NULL;
+
+ ret = heim_get_default_config_files(def, envvar, &defpp);
+ if (ret)
+ return ret;
+
+ ret = heim_prepend_config_files(prepend, defpp, &pp);
+ heim_free_config_files(defpp);
+ if (ret) {
+ return ret;
+ }
+ *pfilenames = pp;
+ return 0;
+}
+
+heim_error_code
+heim_get_default_config_files(const char *def,
+ const char *envvar,
+ char ***pfilenames)
+{
+ const char *files = NULL;
+
+ files = secure_getenv(envvar);
+
+#ifdef _WIN32
+ if (files == NULL) {
+ char * reg_files;
+ reg_files = get_default_config_config_files_from_registry(envvar);
+ if (reg_files != NULL) {
+ heim_error_code code;
+
+ code = heim_prepend_config_files(reg_files, NULL, pfilenames);
+ free(reg_files);
+
+ return code;
+ }
+ }
+#endif
+
+ if (files == NULL)
+ files = def;
+ return heim_prepend_config_files(files, NULL, pfilenames);
+}
+
+#ifdef _WIN32
+#define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
+#define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
+#endif
+
+heim_error_code
+heim_set_config_files(heim_context context, char **filenames,
+ heim_config_binding **res)
+{
+ heim_error_code ret = 0;
+
+ *res = NULL;
+ while (filenames != NULL && *filenames != NULL && **filenames != '\0') {
+ ret = heim_config_parse_file_multi(context, *filenames, res);
+ if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM
+ && ret != HEIM_ERR_CONFIG_BADFORMAT) {
+ heim_config_file_free(context, *res);
+ *res = NULL;
+ return ret;
+ }
+ filenames++;
+ }
+
+#ifdef _WIN32
+ /*
+ * We always ignored errors from loading from the registry, so we still do.
+ */
+ heim_load_config_from_registry(context, REGPATH_KERBEROS,
+ REGPATH_HEIMDAL, res);
+
+#endif
+ return 0;
+}
+
+void
+heim_free_config_files(char **filenames)
+{
+ char **p;
+
+ for (p = filenames; p && *p != NULL; p++)
+ free(*p);
+ free(filenames);
+}
diff --git a/lib/base/data.c b/lib/base/data.c
index 4aa6efc66774..cefdde0c1bbe 100644
--- a/lib/base/data.c
+++ b/lib/base/data.c
@@ -34,7 +34,7 @@
#include "baselocl.h"
#include <string.h>
-static void
+static void HEIM_CALLCONV
data_dealloc(void *ptr)
{
heim_data_t d = ptr;
@@ -61,7 +61,7 @@ data_cmp(void *a, void *b)
return memcmp(osa->data, osb->data, osa->length);
}
-static unsigned long
+static uintptr_t
data_hash(void *ptr)
{
heim_octet_string *os = ptr;
@@ -69,8 +69,9 @@ data_hash(void *ptr)
if (os->length < 4)
return os->length;
- return s[0] | (s[1] << 8) |
- (s[os->length - 2] << 16) | (s[os->length - 1] << 24);
+
+ return ((unsigned long)s[os->length - 1] << 24)
+ | (s[os->length - 2] << 16) | (s[1] << 8) | s[0];
}
struct heim_type_data _heim_data_object = {
diff --git a/lib/base/db.c b/lib/base/db.c
index 944091684882..e6f6af41a203 100644
--- a/lib/base/db.c
+++ b/lib/base/db.c
@@ -47,6 +47,8 @@
* memory-based rollback log is used).
*/
+#include "config.h"
+
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -84,7 +86,7 @@ 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);
+static void HEIM_CALLCONV db_dealloc(void *ptr);
struct heim_type_data db_object = {
HEIM_TID_DB,
@@ -150,7 +152,7 @@ db_init_plugins_once(void *arg)
db_plugins = heim_retain(arg);
}
-static void
+static void HEIM_CALLCONV
plugin_dealloc(void *arg)
{
db_plugin plug = arg;
@@ -242,7 +244,7 @@ heim_db_register(const char *dbtype,
return ret;
}
-static void
+static void HEIM_CALLCONV
db_dealloc(void *arg)
{
heim_db_t db = arg;
@@ -577,7 +579,7 @@ heim_db_commit(heim_db_t db, heim_error_t *error)
goto done;
}
- if (db->options == NULL)
+ if (db->options)
journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename"));
if (journal_fname != NULL) {
@@ -1144,21 +1146,15 @@ enomem:
static
heim_data_t from_base64(heim_string_t s, heim_error_t *error)
{
+ ssize_t len = -1;
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:
+ if (buf)
+ len = rk_base64_decode(heim_string_get_utf8(s), buf);
+ if (len > -1 && (d = heim_data_ref_create(buf, len, free)))
+ return d;
free(buf);
if (error)
*error = heim_error_create_enomem();
@@ -1327,7 +1323,7 @@ json_db_open(void *plug, const char *dbtype, const char *dbname,
if (error)
*error = NULL;
- if (dbtype && *dbtype && strcmp(dbtype, "json"))
+ if (dbtype && *dbtype && strcmp(dbtype, "json") != 0)
return HEIM_ERROR(error, EINVAL, (EINVAL, N_("Wrong DB type", "")));
if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0) {
char *ext = strrchr(dbname, '.');
@@ -1511,11 +1507,12 @@ json_db_sync(void *db, heim_error_t *error)
json = heim_json_copy_serialize(jsondb->dict, 0, &e);
if (json == NULL) {
+ ret = heim_error_get_code(e);
if (error)
*error = e;
else
heim_release(e);
- return heim_error_get_code(e);
+ return ret;
}
json_text = heim_string_get_utf8(json);
diff --git a/lib/base/dict.c b/lib/base/dict.c
index 8d73846b2bbb..86be109ffc5e 100644
--- a/lib/base/dict.c
+++ b/lib/base/dict.c
@@ -47,7 +47,7 @@ struct heim_dict_data {
struct hashentry **tab;
};
-static void
+static void HEIM_CALLCONV
dict_dealloc(void *ptr)
{
heim_dict_t dict = ptr;
@@ -115,6 +115,8 @@ heim_dict_create(size_t size)
heim_dict_t dict;
dict = _heim_alloc_object(&dict_object, sizeof(*dict));
+ if (dict == NULL)
+ return NULL;
dict->size = findprime(size);
if (dict->size == 0) {
@@ -149,7 +151,7 @@ heim_dict_get_type_id(void)
static struct hashentry *
_search(heim_dict_t dict, heim_object_t ptr)
{
- unsigned long v = heim_get_hash(ptr);
+ uintptr_t v = heim_get_hash(ptr);
struct hashentry *p;
for (p = dict->tab[v % dict->size]; p != NULL; p = p->next)
@@ -219,7 +221,7 @@ heim_dict_set_value(heim_dict_t dict, heim_object_t key, heim_object_t value)
heim_release(h->value);
h->value = heim_retain(value);
} else {
- unsigned long v;
+ uintptr_t v;
h = malloc(sizeof(*h));
if (h == NULL)
diff --git a/lib/base/error.c b/lib/base/error.c
index 8ae65de4981e..bc289d3d910a 100644
--- a/lib/base/error.c
+++ b/lib/base/error.c
@@ -41,7 +41,7 @@ struct heim_error {
struct heim_error *next;
};
-static void
+static void HEIM_CALLCONV
error_dealloc(void *ptr)
{
struct heim_error *p = ptr;
@@ -58,7 +58,7 @@ error_cmp(void *a, void *b)
return heim_cmp(ap->msg, bp->msg);
}
-static unsigned long
+static uintptr_t
error_hash(void *ptr)
{
struct heim_error *p = ptr;
@@ -86,6 +86,7 @@ heim_error_create_enomem(void)
void
heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4))
{
if (error) {
va_list ap;
@@ -97,6 +98,7 @@ heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
heim_error_t
heim_error_create(int error_code, const char *fmt, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3))
{
heim_error_t e;
va_list ap;
@@ -110,6 +112,7 @@ heim_error_create(int error_code, const char *fmt, ...)
heim_error_t
heim_error_createv(int error_code, const char *fmt, va_list ap)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0))
{
heim_error_t e;
char *str;
diff --git a/lib/base/error_string.c b/lib/base/error_string.c
new file mode 100644
index 000000000000..a562833a91a0
--- /dev/null
+++ b/lib/base/error_string.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2001, 2003, 2005 - 2020 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"
+
+#undef __attribute__
+#define __attribute__(x)
+
+void
+heim_clear_error_message(heim_context context)
+{
+ if (!context)
+ return;
+ if (context->error_string)
+ free(context->error_string);
+ context->error_code = 0;
+ context->error_string = NULL;
+}
+
+void
+heim_set_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (context)
+ heim_vset_error_message(context, ret, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_vset_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, va_list args)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ int r;
+
+ if (context == NULL)
+ return;
+ if (context->error_string) {
+ free(context->error_string);
+ context->error_string = NULL;
+ }
+ context->error_code = ret;
+ r = vasprintf(&context->error_string, fmt, args);
+ if (r < 0)
+ context->error_string = NULL;
+ if (context->error_string)
+ heim_debug(context, 200, "error message: %s: %d", context->error_string, ret);
+}
+
+void
+heim_prepend_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ heim_vprepend_error_message(context, ret, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_vprepend_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, va_list args)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ char *str = NULL, *str2 = NULL;
+
+ if (context == NULL || context->error_code != ret ||
+ vasprintf(&str, fmt, args) < 0 || str == NULL)
+ return;
+ if (context->error_string) {
+ int e;
+
+ e = asprintf(&str2, "%s: %s", str, context->error_string);
+ free(context->error_string);
+ if (e < 0 || str2 == NULL)
+ context->error_string = NULL;
+ else
+ context->error_string = str2;
+ free(str);
+ } else
+ context->error_string = str;
+}
+
+const char *
+heim_get_error_message(heim_context context, heim_error_code code)
+{
+ const char *cstr = NULL;
+ char *str = NULL;
+ char buf[128];
+ int free_context = 0;
+
+ if (code == 0)
+ return strdup("Success");
+
+ /*
+ * The MIT version of this function ignores the krb5_context
+ * and several widely deployed applications call krb5_get_error_message()
+ * with a NULL context in order to translate an error code as a
+ * replacement for error_message(). Another reason a NULL context
+ * might be provided is if the krb5_init_context() call itself
+ * failed.
+ */
+ if (context &&
+ context->error_string &&
+ (code == context->error_code || context->error_code == 0) &&
+ (cstr = strdup(context->error_string)))
+ return cstr;
+
+ if (context == NULL && (context = heim_context_init()))
+ free_context = 1;
+ if (context)
+ cstr = com_right_r(context->et_list, code, buf, sizeof(buf));
+ if (free_context)
+ heim_context_free(&context);
+
+ if (cstr || (cstr = error_message(code)))
+ return strdup(cstr);
+ if (asprintf(&str, "<unknown error: %d>", (int)code) == -1 || str == NULL)
+ return NULL;
+ return str;
+}
+
+const char *
+heim_get_error_string(heim_context context)
+{
+ if (context && context->error_string)
+ return strdup(context->error_string);
+ return NULL;
+}
+
+int
+heim_have_error_string(heim_context context)
+{
+ return context && context->error_string != NULL;
+}
+
+void
+heim_free_error_message(heim_context context, const char *msg)
+{
+ free(rk_UNCONST(msg));
+}
diff --git a/lib/base/expand_path.c b/lib/base/expand_path.c
new file mode 100644
index 000000000000..cf249917e8fe
--- /dev/null
+++ b/lib/base/expand_path.c
@@ -0,0 +1,725 @@
+
+/***********************************************************************
+ * Copyright (c) 2009-2020, 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 <stdarg.h>
+
+typedef int PTYPE;
+
+#ifdef _WIN32
+#include <shlobj.h>
+#include <sddl.h>
+
+/*
+ * Expand a %{TEMP} token
+ *
+ * The %{TEMP} token expands to the temporary path for the current
+ * user as returned by GetTempPath().
+ *
+ * @note: Since the GetTempPath() function relies on the TMP or TEMP
+ * environment variables, this function will failover to the system
+ * temporary directory until the user profile is loaded. In addition,
+ * the returned path may or may not exist.
+ */
+static heim_error_code
+expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR tpath[MAX_PATH];
+ size_t len;
+
+ if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
+ heim_set_error_message(context, EINVAL,
+ "Failed to get temporary path (GLE=%d)",
+ GetLastError());
+ return EINVAL;
+ }
+
+ len = strlen(tpath);
+
+ if (len > 0 && tpath[len - 1] == '\\')
+ tpath[len - 1] = '\0';
+
+ *ret = strdup(tpath);
+
+ if (*ret == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+
+/*
+ * Expand a %{BINDIR} token
+ *
+ * This is also used to expand a few other tokens on Windows, since
+ * most of the executable binaries end up in the same directory. The
+ * "bin" directory is considered to be the directory in which the
+ * containing DLL is located.
+ */
+static heim_error_code
+expand_bin_dir(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR path[MAX_PATH];
+ TCHAR *lastSlash;
+ DWORD nc;
+
+ nc = GetModuleFileName((HINSTANCE)&__ImageBase, path,
+ sizeof(path)/sizeof(path[0]));
+ if (nc == 0 ||
+ nc == sizeof(path)/sizeof(path[0])) {
+ return EINVAL;
+ }
+
+ lastSlash = strrchr(path, '\\');
+ if (lastSlash != NULL) {
+ TCHAR *fslash = strrchr(lastSlash, '/');
+
+ if (fslash != NULL)
+ lastSlash = fslash;
+
+ *lastSlash = '\0';
+ }
+
+ if (postfix) {
+ if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
+ return EINVAL;
+ }
+
+ *ret = strdup(path);
+ if (*ret == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+/*
+ * Expand a %{USERID} token
+ *
+ * The %{USERID} token expands to the string representation of the
+ * user's SID. The user account that will be used is the account
+ * corresponding to the current thread's security token. This means
+ * that:
+ *
+ * - If the current thread token has the anonymous impersonation
+ * level, the call will fail.
+ *
+ * - If the current thread is impersonating a token at
+ * SecurityIdentification level the call will fail.
+ *
+ */
+static heim_error_code
+expand_userid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ int rv = EINVAL;
+ HANDLE hThread = NULL;
+ HANDLE hToken = NULL;
+ PTOKEN_OWNER pOwner = NULL;
+ DWORD len = 0;
+ LPTSTR strSid = NULL;
+
+ hThread = GetCurrentThread();
+
+ if (!OpenThreadToken(hThread, TOKEN_QUERY,
+ FALSE, /* Open the thread token as the
+ current thread user. */
+ &hToken)) {
+
+ DWORD le = GetLastError();
+
+ if (le == ERROR_NO_TOKEN) {
+ HANDLE hProcess = GetCurrentProcess();
+
+ le = 0;
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
+ le = GetLastError();
+ }
+
+ if (le != 0) {
+ heim_set_error_message(context, rv,
+ "Can't open thread token (GLE=%d)", le);
+ goto _exit;
+ }
+ }
+
+ if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ heim_set_error_message(context, rv,
+ "Unexpected error reading token information (GLE=%d)",
+ GetLastError());
+ goto _exit;
+ }
+
+ if (len == 0) {
+ heim_set_error_message(context, rv,
+ "GetTokenInformation() returned truncated buffer");
+ goto _exit;
+ }
+
+ pOwner = malloc(len);
+ if (pOwner == NULL) {
+ heim_set_error_message(context, rv, "Out of memory");
+ goto _exit;
+ }
+ } else {
+ heim_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
+ goto _exit;
+ }
+
+ if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
+ heim_set_error_message(context, rv,
+ "GetTokenInformation() failed. GLE=%d",
+ GetLastError());
+ goto _exit;
+ }
+
+ if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
+ heim_set_error_message(context, rv,
+ "Can't convert SID to string. GLE=%d",
+ GetLastError());
+ goto _exit;
+ }
+
+ *ret = strdup(strSid);
+ if (*ret == NULL)
+ heim_set_error_message(context, rv, "Out of memory");
+
+ rv = 0;
+
+ _exit:
+ if (hToken != NULL)
+ CloseHandle(hToken);
+
+ if (pOwner != NULL)
+ free (pOwner);
+
+ if (strSid != NULL)
+ LocalFree(strSid);
+
+ return rv;
+}
+
+/*
+ * Expand a folder identified by a CSIDL
+ */
+
+static heim_error_code
+expand_csidl(heim_context context, PTYPE folder, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR path[MAX_PATH];
+ size_t len;
+
+ if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
+ heim_set_error_message(context, EINVAL, "Unable to determine folder path");
+ return EINVAL;
+ }
+
+ len = strlen(path);
+
+ if (len > 0 && path[len - 1] == '\\')
+ path[len - 1] = '\0';
+
+ if (postfix &&
+ strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
+ return heim_enomem(context);
+
+ *ret = strdup(path);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+#else
+
+static heim_error_code
+expand_path(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ *ret = strdup(postfix);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ const char *p = NULL;
+
+ p = secure_getenv("TEMP");
+
+ if (p)
+ *ret = strdup(p);
+ else
+ *ret = strdup("/tmp");
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_userid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ int ret = asprintf(str, "%ld", (unsigned long)getuid());
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_euid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ int ret = asprintf(str, "%ld", (unsigned long)geteuid());
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+#endif /* _WIN32 */
+
+static heim_error_code
+expand_home(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char homedir[MAX_PATH];
+ int ret;
+
+ if (roken_get_homedir(homedir, sizeof(homedir)))
+ ret = asprintf(str, "%s", homedir);
+ else
+ ret = asprintf(str, "/unknown");
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_username(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char user[128];
+ const char *username = roken_get_username(user, sizeof(user));
+
+ if (username == NULL) {
+ heim_set_error_message(context, ENOTTY,
+ N_("unable to figure out current principal",
+ ""));
+ return ENOTTY; /* XXX */
+ }
+
+ *str = strdup(username);
+ if (*str == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+static heim_error_code
+expand_loginname(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char user[128];
+ const char *username = roken_get_loginname(user, sizeof(user));
+
+ if (username == NULL) {
+ heim_set_error_message(context, ENOTTY,
+ N_("unable to figure out current principal",
+ ""));
+ return ENOTTY; /* XXX */
+ }
+
+ *str = strdup(username);
+ if (*str == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+static heim_error_code
+expand_strftime(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ size_t len;
+ time_t t;
+ char buf[1024];
+
+ t = time(NULL);
+ len = strftime(buf, sizeof(buf), arg, localtime(&t));
+ if (len == 0 || len >= sizeof(buf))
+ return heim_enomem(context);
+ *ret = strdup(buf);
+ return 0;
+}
+
+/**
+ * Expand an extra token
+ */
+
+static heim_error_code
+expand_extra_token(heim_context context, const char *value, char **ret)
+{
+ *ret = strdup(value);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+/**
+ * Expand a %{null} token
+ *
+ * The expansion of a %{null} token is always the empty string.
+ */
+
+static heim_error_code
+expand_null(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ *ret = strdup("");
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+
+static const struct {
+ const char * tok;
+ int ftype;
+#define FTYPE_CSIDL 0
+#define FTYPE_SPECIAL 1
+
+ PTYPE param;
+ const char * postfix;
+
+ int (*exp_func)(heim_context, PTYPE, const char *, const char *, char **);
+
+#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
+#define SPECIAL(f) SPECIALP(f, NULL)
+
+} tokens[] = {
+#ifdef _WIN32
+#define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl
+#define CSIDL(C) CSIDLP(C, NULL)
+
+ {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
+ {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
+ {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
+ {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
+ {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
+ {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
+ {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
+ {"LIBDIR", SPECIAL(expand_bin_dir)},
+ {"BINDIR", SPECIAL(expand_bin_dir)},
+ {"LIBEXEC", SPECIAL(expand_bin_dir)},
+ {"SBINDIR", SPECIAL(expand_bin_dir)},
+#else
+ {"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, expand_path},
+ {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path},
+ {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path},
+ {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, expand_path},
+ {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path},
+ {"USERCONFIG", SPECIAL(expand_home)}, /* same as %{HOME} on not-Windows */
+ {"euid", SPECIAL(expand_euid)},
+ {"ruid", SPECIAL(expand_userid)},
+ {"loginname", SPECIAL(expand_loginname)},
+#endif
+ {"username", SPECIAL(expand_username)},
+ {"TEMP", SPECIAL(expand_temp_folder)},
+ {"USERID", SPECIAL(expand_userid)},
+ {"uid", SPECIAL(expand_userid)},
+ {"null", SPECIAL(expand_null)},
+ {"strftime", SPECIAL(expand_strftime)},
+ {"HOME", SPECIAL(expand_home)},
+};
+
+static heim_error_code
+expand_token(heim_context context,
+ const char *token,
+ const char *token_end,
+ char **extra_tokens,
+ char **ret)
+{
+ heim_error_code errcode;
+ size_t i;
+ char **p;
+ const char *colon;
+
+ *ret = NULL;
+
+ if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
+ token_end - token <= 2) {
+ heim_set_error_message(context, EINVAL,"Invalid token.");
+ return EINVAL;
+ }
+
+ for (p = extra_tokens; p && p[0]; p += 2) {
+ if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
+ return expand_extra_token(context, p[1], ret);
+ }
+
+ for (colon=token+2; colon < token_end; colon++)
+ if (*colon == ':')
+ break;
+
+ for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
+ if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) {
+ char *arg = NULL;
+
+ errcode = 0;
+ if (*colon == ':') {
+ int asprintf_ret = asprintf(&arg, "%.*s",
+ (int)(token_end - colon - 1),
+ colon + 1);
+ if (asprintf_ret < 0 || !arg)
+ errcode = ENOMEM;
+ }
+ if (!errcode)
+ errcode = tokens[i].exp_func(context, tokens[i].param,
+ tokens[i].postfix, arg, ret);
+ free(arg);
+ return errcode;
+ }
+
+ heim_set_error_message(context, EINVAL, "Invalid token.");
+ return EINVAL;
+}
+
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Params:
+ *
+ * @context A heim_context
+ * @path_in The path to expand tokens from
+ * @filepath True if this is a filesystem path (converts slashes to
+ * backslashes on Windows)
+ * @ppath_out The expanded path
+ * @... Variable number of pairs of strings, the first of each
+ * being a token (e.g., "luser") and the second a string to
+ * replace it with. The list is terminated by a NULL.
+ */
+heim_error_code
+heim_expand_path_tokens(heim_context context,
+ const char *path_in,
+ int filepath,
+ char **ppath_out,
+ ...)
+{
+ heim_error_code ret;
+ va_list ap;
+
+ va_start(ap, ppath_out);
+ ret = heim_expand_path_tokensv(context, path_in, filepath, ppath_out, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static void
+free_extra_tokens(char **extra_tokens)
+{
+ char **p;
+
+ for (p = extra_tokens; p && *p; p++)
+ free(*p);
+ free(extra_tokens);
+}
+
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Inputs:
+ *
+ * @context A heim_context
+ * @path_in The path to expand tokens from
+ * @filepath True if this is a filesystem path (converts slashes to
+ * backslashes on Windows)
+ * @ppath_out The expanded path
+ * @ap A NULL-terminated va_list of pairs of strings, the first of each
+ * being a token (e.g., "luser") and the second a string to replace
+ * it with.
+ *
+ * Outputs:
+ *
+ * @ppath_out Path with expanded tokens (caller must free() this)
+ */
+heim_error_code
+heim_expand_path_tokensv(heim_context context,
+ const char *path_in,
+ int filepath,
+ char **ppath_out, va_list ap)
+{
+ char *tok_begin, *tok_end, *append;
+ char **extra_tokens = NULL;
+ const char *path_left;
+ size_t nargs = 0;
+ size_t len = 0;
+ va_list ap2;
+
+ if (path_in == NULL || *path_in == '\0') {
+ *ppath_out = strdup("");
+ return 0;
+ }
+
+ *ppath_out = NULL;
+
+#if defined(_MSC_VER)
+ ap2 = ap; /* Come on! See SO #558223 */
+#else
+ va_copy(ap2, ap);
+#endif
+ while (va_arg(ap2, const char *)) {
+ nargs++;
+ va_arg(ap2, const char *);
+ }
+ va_end(ap2);
+ nargs *= 2;
+
+ /* Get extra tokens */
+ if (nargs) {
+ size_t i;
+
+ extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
+ if (extra_tokens == NULL)
+ return heim_enomem(context);
+ for (i = 0; i < nargs; i++) {
+ const char *s = va_arg(ap, const char *); /* token key */
+ if (s == NULL)
+ break;
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i++] == NULL) {
+ free_extra_tokens(extra_tokens);
+ return heim_enomem(context);
+ }
+ s = va_arg(ap, const char *); /* token value */
+ if (s == NULL)
+ s = "";
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i] == NULL) {
+ free_extra_tokens(extra_tokens);
+ return heim_enomem(context);
+ }
+ }
+ }
+
+ for (path_left = path_in; path_left && *path_left; ) {
+
+ tok_begin = strstr(path_left, "%{");
+
+ if (tok_begin && tok_begin != path_left) {
+
+ append = malloc((tok_begin - path_left) + 1);
+ if (append) {
+ memcpy(append, path_left, tok_begin - path_left);
+ append[tok_begin - path_left] = '\0';
+ }
+ path_left = tok_begin;
+
+ } else if (tok_begin) {
+
+ tok_end = strchr(tok_begin, '}');
+ if (tok_end == NULL) {
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ heim_set_error_message(context, EINVAL, "variable missing }");
+ return EINVAL;
+ }
+
+ if (expand_token(context, tok_begin, tok_end, extra_tokens,
+ &append)) {
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return EINVAL;
+ }
+
+ path_left = tok_end + 1;
+ } else {
+
+ append = strdup(path_left);
+ path_left = NULL;
+
+ }
+
+ if (append == NULL) {
+
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return heim_enomem(context);
+
+ }
+
+ {
+ size_t append_len = strlen(append);
+ char * new_str = realloc(*ppath_out, len + append_len + 1);
+
+ if (new_str == NULL) {
+ free_extra_tokens(extra_tokens);
+ free(append);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return heim_enomem(context);
+ }
+
+ *ppath_out = new_str;
+ memcpy(*ppath_out + len, append, append_len + 1);
+ len = len + append_len;
+ free(append);
+ }
+ }
+
+#ifdef _WIN32
+ /* Also deal with slashes */
+ if (filepath && *ppath_out) {
+ char * c;
+
+ for (c = *ppath_out; *c; c++)
+ if (*c == '/')
+ *c = '\\';
+ }
+#endif
+
+ free_extra_tokens(extra_tokens);
+ return 0;
+}
diff --git a/lib/base/heim_err.et b/lib/base/heim_err.et
new file mode 100644
index 000000000000..5532d9b84dbb
--- /dev/null
+++ b/lib/base/heim_err.et
@@ -0,0 +1,57 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table heim
+
+prefix HEIM_ERR
+
+error_code LOG_PARSE, "Error parsing log destination"
+error_code V4_PRINC_NO_CONV, "Failed to convert v4 principal"
+error_code SALTTYPE_NOSUPP, "Salt type is not supported by enctype"
+error_code NOHOST, "Host not found"
+error_code OPNOTSUPP, "Operation not supported"
+error_code EOF, "End of file"
+error_code BAD_MKEY, "Failed to get the master key"
+error_code SERVICE_NOMATCH, "Unacceptable service used"
+error_code NOT_SEEKABLE, "File descriptor not seekable"
+error_code TOO_BIG, "Offset too large"
+error_code BAD_HDBENT_ENCODING, "Invalid HDB entry encoding"
+error_code RANDOM_OFFLINE, "No random source available"
+error_code CONFIG_BADFORMAT, "Improper format of configuration file"
+error_code PA_CONTINUE_NEEDED, "Need to continue preauth stepping"
+error_code PA_CANT_CONTINUE, "Can't continue with this preauth"
+error_code NO_MORE_PA_MECHS, "No more PA mechanisms available"
+
+index 64
+prefix HEIM_PKINIT
+error_code NO_CERTIFICATE, "Certificate missing"
+error_code NO_PRIVATE_KEY, "Private key missing"
+error_code NO_VALID_CA, "No valid certificate authority"
+error_code CERTIFICATE_INVALID, "Certificate invalid"
+error_code PRIVATE_KEY_INVALID, "Private key invalid"
+
+index 128
+prefix HEIM_EAI
+#error_code NOERROR, "no error"
+error_code UNKNOWN, "unknown error from getaddrinfo"
+error_code ADDRFAMILY, "address family for nodename not supported"
+error_code AGAIN, "temporary failure in name resolution"
+error_code BADFLAGS, "invalid value for ai_flags"
+error_code FAIL, "non-recoverable failure in name resolution"
+error_code FAMILY, "ai_family not supported"
+error_code MEMORY, "memory allocation failure"
+error_code NODATA, "no address associated with nodename"
+error_code NONAME, "nodename nor servname provided, or not known"
+error_code SERVICE, "servname not supported for ai_socktype"
+error_code SOCKTYPE, "ai_socktype not supported"
+error_code SYSTEM, "system error returned in errno"
+
+index 192
+prefix HEIM_NET
+error_code CONN_REFUSED, "connection refused"
+
+end
diff --git a/lib/base/heimbase-atomics.h b/lib/base/heimbase-atomics.h
new file mode 100644
index 000000000000..e468a36bda9a
--- /dev/null
+++ b/lib/base/heimbase-atomics.h
@@ -0,0 +1,386 @@
+/*
+ * 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_ATOMICS_H
+#define HEIM_BASE_ATOMICS_H 1
+
+#include <stdint.h>
+
+/*
+ * Atomic operations
+ *
+ * (#define HEIM_BASE_ATOMICS_FALLBACK to test fallbacks.)
+ */
+
+#if !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(HAVE_STDATOMIC_H)
+
+#include <stdatomic.h>
+
+#define heim_base_atomic_init(t, v) atomic_init(t, v)
+#define heim_base_atomic_load(x) atomic_load((x))
+#define heim_base_atomic_store(t, v) atomic_store((t), (v))
+
+#define heim_base_atomic(T) _Atomic(T)
+
+#define heim_base_atomic_inc_32(x) (atomic_fetch_add((x), 1) + 1)
+#define heim_base_atomic_dec_32(x) (atomic_fetch_sub((x), 1) - 1)
+#define heim_base_atomic_inc_64(x) (atomic_fetch_add((x), 1) + 1)
+#define heim_base_atomic_dec_64(x) (atomic_fetch_sub((x), 1) - 1)
+
+#define heim_base_exchange_pointer(t,v) atomic_exchange((t), (v))
+#define heim_base_exchange_32(t,v) atomic_exchange((t), (v))
+#define heim_base_exchange_64(t,v) atomic_exchange((t), (v))
+
+/*
+ * <stdatomic.h>'s and AIX's CAS functions take a pointer to an expected value
+ * and return a boolean, setting the pointed-to variable to the old value of
+ * the target.
+ *
+ * Other CAS functions, like GCC's, Solaris'/Illumos', and Windows', return the
+ * old value and don't take a pointer to an expected value.
+ *
+ * We implement the latter semantics.
+ */
+static inline void *
+heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d)
+{
+ return atomic_compare_exchange_strong(t, &e, d), e;
+}
+
+static inline uint32_t
+heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d)
+{
+ return atomic_compare_exchange_strong(t, &e, d), e;
+}
+
+static inline uint64_t
+heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d)
+{
+ return atomic_compare_exchange_strong(t, &e, d), e;
+}
+
+#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d))
+#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d))
+#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d))
+
+#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH)
+
+#define heim_base_atomic_barrier() __sync_synchronize()
+
+#define heim_base_atomic_inc_32(x) __sync_add_and_fetch((x), 1)
+#define heim_base_atomic_dec_32(x) __sync_sub_and_fetch((x), 1)
+#define heim_base_atomic_inc_64(x) __sync_add_and_fetch((x), 1)
+#define heim_base_atomic_dec_64(x) __sync_sub_and_fetch((x), 1)
+
+#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
+/* FIXME: some targets may only write the value 1 into *t */
+#define heim_base_exchange_pointer(t,v) __sync_lock_test_and_set((t), (v))
+#endif
+
+#define heim_base_exchange_32(t,v) heim_base_exchange_pointer((t), (v))
+#define heim_base_exchange_64(t,v) heim_base_exchange_pointer((t), (v))
+
+#define heim_base_cas_pointer(t,e,d) __sync_val_compare_and_swap((t), (e), (d))
+#define heim_base_cas_32(t,e,d) __sync_val_compare_and_swap((t), (e), (d))
+#define heim_base_cas_64(t,e,d) __sync_val_compare_and_swap((t), (e), (d))
+
+#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(__sun)
+
+#include <sys/atomic.h>
+#include <mbarrier.h>
+
+static inline void __heim_base_atomic_barrier(void)
+{
+ __machine_rw_barrier();
+}
+
+#define heim_base_atomic_barrier() __heim_base_atomic_barrier()
+
+#define heim_base_atomic(T) volatile T
+
+#define heim_base_atomic_inc_32(x) atomic_inc_32_nv((x))
+#define heim_base_atomic_dec_32(x) atomic_dec_32_nv((x))
+#define heim_base_atomic_inc_64(x) atomic_inc_64_nv((x))
+#define heim_base_atomic_dec_64(x) atomic_dec_64_nv((x))
+
+#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((t), (void *)(v))
+#define heim_base_exchange_32(t,v) atomic_swap_32((t), (v))
+#define heim_base_exchange_64(t,v) atomic_swap_64((t), (v))
+
+#define heim_base_cas_pointer(t,e,d) atomic_cas_ptr((t), (e), (d))
+#define heim_base_cas_32(t,e,d) atomic_cas_32((t), (e), (d))
+#define heim_base_cas_64(t,e,d) atomic_cas_64((t), (e), (d))
+
+#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(_AIX)
+
+#include <sys/atomic_op.h>
+
+#define heim_base_atomic_barrier() __isync()
+
+#define heim_base_atomic_inc_32(x) (fetch_and_add((atomic_p)(x), 1) + 1)
+#define heim_base_atomic_dec_32(x) (fetch_and_add((atomic_p)(x), -1) - 1)
+#define heim_base_atomic_inc_64(x) (fetch_and_addlp((atomic_l)(x), 1) + 1)
+#define heim_base_atomic_dec_64(x) (fetch_and_addlp((atomic_l)(x), -1) - 1)
+
+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;
+}
+
+static inline uint32_t
+heim_base_exchange_32(uint32_t *p, uint32_t newval)
+{
+ uint32_t val = *p;
+
+ while (!compare_and_swap((atomic_p)p, (int *)&val, (int)newval))
+ ;
+
+ return val;
+}
+
+static inline uint64_t
+heim_base_exchange_64(uint64_t *p, uint64_t newval)
+{
+ uint64_t val = *p;
+
+ while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval))
+ ;
+
+ return val;
+}
+
+static inline void *
+heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d)
+{
+ return compare_and_swaplp((atomic_l)t, &e, d), e;
+}
+
+static inline uint32_t
+heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d)
+{
+ return compare_and_swap((atomic_p)t, &e, d), e;
+}
+
+static inline uint64_t
+heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d)
+{
+ return compare_and_swaplp((atomic_l)t, &e, d), e;
+}
+
+#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d))
+#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d))
+#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d))
+
+#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(_WIN32)
+
+#define heim_base_atomic_barrier() MemoryBarrier()
+
+#define heim_base_atomic_inc_32(x) InterlockedIncrement(x)
+#define heim_base_atomic_dec_32(x) InterlockedDecrement(x)
+#define heim_base_atomic_inc_64(x) InterlockedIncrement64(x)
+#define heim_base_atomic_dec_64(x) InterlockedDecrement64(x)
+
+#define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((PVOID volatile *)(t), (PVOID)(v))
+#define heim_base_exchange_32(t,v) ((ULONG)InterlockedExchange((LONG volatile *)(t), (LONG)(v)))
+#define heim_base_exchange_64(t,v) ((ULONG64)InterlockedExchange64((ULONG64 volatile *)(t), (LONG64)(v)))
+
+#define heim_base_cas_pointer(t,e,d) InterlockedCompareExchangePointer((PVOID volatile *)(t), (d), (e))
+#define heim_base_cas_32(t,e,d) InterlockedCompareExchange ((LONG volatile *)(t), (d), (e))
+#define heim_base_cas_64(t,e,d) InterlockedCompareExchange64((ULONG64 volatile *)(t), (d), (e))
+
+#else
+
+#define heim_base_atomic(T) volatile T
+#define heim_base_atomic_barrier()
+#define heim_base_atomic_load(x) (*(x))
+#define heim_base_atomic_init(t, v) do { (*(t) = (v)); } while (0)
+#define heim_base_atomic_store(t, v) do { (*(t) = (v)); } while (0)
+
+#include <heim_threads.h>
+
+#define HEIM_BASE_NEED_ATOMIC_MUTEX 1
+
+static inline uint32_t
+heim_base_atomic_inc_32(heim_base_atomic(uint32_t) *x)
+{
+ uint32_t t;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ t = ++(*x);
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return t;
+}
+
+static inline uint32_t
+heim_base_atomic_dec_32(heim_base_atomic(uint32_t) *x)
+{
+ uint32_t t;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ t = --(*x);
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return t;
+}
+
+static inline uint64_t
+heim_base_atomic_inc_64(heim_base_atomic(uint64_t) *x)
+{
+ uint64_t t;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ t = ++(*x);
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return t;
+}
+
+static inline uint64_t
+heim_base_atomic_dec_64(heim_base_atomic(uint64_t) *x)
+{
+ uint64_t t;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ t = --(*x);
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return t;
+}
+
+static inline void *
+heim_base_exchange_pointer(heim_base_atomic(void *)target, void *value)
+{
+ void *old;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ old = *(void **)target;
+ *(void **)target = value;
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return old;
+}
+
+static inline uint32_t
+heim_base_exchange_32(heim_base_atomic(uint32_t) *target, uint32_t newval)
+{
+ uint32_t old;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ old = *target;
+ *target = newval;
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return old;
+}
+
+static inline uint64_t
+heim_base_exchange_64(heim_base_atomic(uint64_t) *target, uint64_t newval)
+{
+ uint64_t old;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ old = *target;
+ *target = newval;
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return old;
+}
+
+static inline void *
+heim_base_cas_pointer(heim_base_atomic(void *)target, void *expected, void *desired)
+{
+ void *old;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ if ((old = *(void **)target) == expected)
+ *(void **)target = desired;
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return old;
+}
+
+static inline uint32_t
+heim_base_cas_32(heim_base_atomic(uint32_t) *target, uint32_t expected, uint32_t desired)
+{
+ uint32_t old;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ if ((old = *(uint32_t *)target) == expected)
+ *target = desired;
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return old;
+}
+
+static inline uint64_t
+heim_base_cas_64(heim_base_atomic(uint64_t) *target, uint64_t expected,uint64_t desired)
+{
+ uint64_t old;
+ HEIMDAL_MUTEX_lock(heim_base_mutex());
+ if ((old = *(uint64_t *)target) == expected)
+ *target = desired;
+ HEIMDAL_MUTEX_unlock(heim_base_mutex());
+ return old;
+}
+
+#endif /* defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) */
+
+#ifndef heim_base_atomic
+#define heim_base_atomic(T) T
+#endif
+
+#ifndef heim_base_atomic_barrier
+static inline void heim_base_atomic_barrier(void) { return; }
+#endif
+
+#ifndef heim_base_atomic_load
+#define heim_base_atomic_load(x) (heim_base_atomic_barrier(), *(x))
+#endif
+
+#ifndef heim_base_atomic_init
+#define heim_base_atomic_init(t, v) do { (*(t) = (v)); } while (0)
+#endif
+
+#ifndef heim_base_atomic_store
+#define heim_base_atomic_store(t, v) do { \
+ (*(t) = (v)); \
+ heim_base_atomic_barrier(); \
+ } while (0)
+#endif
+
+#if SIZEOF_TIME_T == 8
+#define heim_base_exchange_time_t(t,v) heim_base_exchange_64((t), (v))
+#elif SIZEOF_TIME_T == 4
+#define heim_base_exchange_time_t(t,v) heim_base_exchange_32((t), (v))
+#else
+#error set SIZEOF_TIME_T for your platform
+#endif
+
+#endif /* HEIM_BASE_ATOMICS_H */
diff --git a/lib/base/heimbase-svc.h b/lib/base/heimbase-svc.h
new file mode 100644
index 000000000000..0e7454c8382c
--- /dev/null
+++ b/lib/base/heimbase-svc.h
@@ -0,0 +1,83 @@
+/*
+ * 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 HEIMBASE_SVC_H
+#define HEIMBASE_SVC_H 1
+
+#include <heimbase.h>
+
+/*
+ * This file is meant to be included in services, which can
+ *
+ * #define heim_pcontext krb5_context
+ *
+ * or whatever is appropriate.
+ */
+
+#define HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS \
+ /* Input */ \
+ heim_pcontext context; \
+ heim_pconfig config; \
+ heim_context hcontext; \
+ heim_log_facility *logf; \
+ const char *from; \
+ struct sockaddr *addr; \
+ int datagram_reply; \
+ heim_octet_string request; \
+ \
+ /* Output */ \
+ heim_octet_string *reply; \
+ unsigned int use_request_t:1; \
+ \
+ /* Common state, to be freed in process.c */ \
+ struct timeval tv_start; \
+ struct timeval tv_end; \
+ const char *reqtype; \
+ char *cname; \
+ char *sname; \
+ const char *e_text; \
+ char *e_text_buf; \
+ heim_string_t reason; \
+ /* auditing key/value store */ \
+ heim_dict_t kv; \
+ heim_dict_t attributes; \
+ int32_t error_code
+
+#define HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(CONTEXT_TYPE) \
+ int minor_version; \
+ int (HEIM_LIB_CALL *init)(CONTEXT_TYPE, void **); \
+ void (HEIM_LIB_CALL *fini)(void *)
+
+#endif /* HEIMBASE_SVC_H */
diff --git a/lib/base/heimbase.c b/lib/base/heimbase.c
index 9a1120b68368..4e9ea1b4b5f6 100644
--- a/lib/base/heimbase.c
+++ b/lib/base/heimbase.c
@@ -34,13 +34,14 @@
*/
#include "baselocl.h"
+#include "heimbase-atomics.h"
#include <syslog.h>
-static heim_base_atomic_type tidglobal = HEIM_TID_USER;
+static heim_base_atomic(uint32_t) tidglobal = HEIM_TID_USER;
struct heim_base {
- heim_type_t isa;
- heim_base_atomic_type ref_cnt;
+ heim_const_type_t isa;
+ heim_base_atomic(uint32_t) ref_cnt;
HEIM_TAILQ_ENTRY(heim_base) autorel;
heim_auto_release_t autorelpool;
uintptr_t isaextra[3];
@@ -48,21 +49,24 @@ struct heim_base {
/* specialized version of base */
struct heim_base_mem {
- heim_type_t isa;
- heim_base_atomic_type ref_cnt;
+ heim_const_type_t isa;
+ heim_base_atomic(uint32_t) ref_cnt;
HEIM_TAILQ_ENTRY(heim_base) autorel;
heim_auto_release_t autorelpool;
const char *name;
- void (*dealloc)(void *);
+ void (HEIM_CALLCONV *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
+HEIMDAL_MUTEX * HEIM_CALLCONV
+heim_base_mutex(void)
+{
+ static HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
+ return &_heim_base_mutex;
+}
/*
* Auto release structure
@@ -83,8 +87,8 @@ struct heim_auto_release {
* @return the same object as passed in
*/
-void *
-heim_retain(void *ptr)
+heim_object_t
+heim_retain(heim_object_t ptr)
{
struct heim_base *p;
@@ -92,10 +96,11 @@ heim_retain(void *ptr)
return ptr;
p = PTR2BASE(ptr);
- if (p->ref_cnt == heim_base_atomic_max)
+
+ if (heim_base_atomic_load(&p->ref_cnt) == UINT32_MAX)
return ptr;
- if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
+ if ((heim_base_atomic_inc_32(&p->ref_cnt) - 1) == 0)
heim_abort("resurection");
return ptr;
}
@@ -109,17 +114,18 @@ heim_retain(void *ptr)
void
heim_release(void *ptr)
{
- heim_base_atomic_type old;
+ heim_base_atomic(uint32_t) old;
struct heim_base *p;
if (ptr == NULL || heim_base_is_tagged(ptr))
return;
p = PTR2BASE(ptr);
- if (p->ref_cnt == heim_base_atomic_max)
+
+ if (heim_base_atomic_load(&p->ref_cnt) == UINT32_MAX)
return;
- old = heim_base_atomic_dec(&p->ref_cnt) + 1;
+ old = heim_base_atomic_dec_32(&p->ref_cnt) + 1;
if (old > 1)
return;
@@ -158,7 +164,7 @@ void
_heim_make_permanent(heim_object_t ptr)
{
struct heim_base *p = PTR2BASE(ptr);
- p->ref_cnt = heim_base_atomic_max;
+ heim_base_atomic_store(&p->ref_cnt, UINT32_MAX);
}
@@ -176,7 +182,7 @@ static heim_type_t tagged_isa[9] = {
NULL
};
-heim_type_t
+heim_const_type_t
_heim_get_isa(heim_object_t ptr)
{
struct heim_base *p;
@@ -200,7 +206,7 @@ _heim_get_isa(heim_object_t ptr)
heim_tid_t
heim_get_tid(heim_object_t ptr)
{
- heim_type_t isa = _heim_get_isa(ptr);
+ heim_const_type_t isa = _heim_get_isa(ptr);
return isa->tid;
}
@@ -212,13 +218,13 @@ heim_get_tid(heim_object_t ptr)
* @return a hash value
*/
-unsigned long
+uintptr_t
heim_get_hash(heim_object_t ptr)
{
- heim_type_t isa = _heim_get_isa(ptr);
+ heim_const_type_t isa = _heim_get_isa(ptr);
if (isa->hash)
return isa->hash(ptr);
- return (unsigned long)ptr;
+ return (uintptr_t)ptr;
}
/**
@@ -235,7 +241,7 @@ int
heim_cmp(heim_object_t a, heim_object_t b)
{
heim_tid_t ta, tb;
- heim_type_t isa;
+ heim_const_type_t isa;
ta = heim_get_tid(a);
tb = heim_get_tid(b);
@@ -255,7 +261,7 @@ heim_cmp(heim_object_t a, heim_object_t b)
* Private - allocates an memory object
*/
-static void
+static void HEIM_CALLCONV
memory_dealloc(void *ptr)
{
if (ptr) {
@@ -266,7 +272,7 @@ memory_dealloc(void *ptr)
}
}
-struct heim_type_data memory_object = {
+static const struct heim_type_data memory_object = {
HEIM_TID_MEMORY,
"memory-object",
NULL,
@@ -319,7 +325,7 @@ _heim_create_type(const char *name,
if (type == NULL)
return NULL;
- type->tid = heim_base_atomic_inc(&tidglobal);
+ type->tid = heim_base_atomic_inc_32(&tidglobal);
type->name = name;
type->init = init;
type->dealloc = dealloc;
@@ -332,7 +338,7 @@ _heim_create_type(const char *name,
}
heim_object_t
-_heim_alloc_object(heim_type_t type, size_t size)
+_heim_alloc_object(heim_const_type_t type, size_t size)
{
/* XXX should use posix_memalign */
struct heim_base *p = calloc(1, size + sizeof(*p));
@@ -347,9 +353,10 @@ _heim_alloc_object(heim_type_t type, size_t size)
void *
_heim_get_isaextra(heim_object_t ptr, size_t idx)
{
- struct heim_base *p = (struct heim_base *)PTR2BASE(ptr);
+ struct heim_base *p;
heim_assert(ptr != NULL, "internal error");
+ p = (struct heim_base *)PTR2BASE(ptr);
if (p->isa == &memory_object)
return NULL;
heim_assert(idx < 3, "invalid private heim_base extra data index");
@@ -500,6 +507,8 @@ heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
void
heim_abort(const char *fmt, ...)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2))
{
va_list ap;
va_start(ap, fmt);
@@ -513,6 +522,8 @@ heim_abort(const char *fmt, ...)
void
heim_abortv(const char *fmt, va_list ap)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0))
{
static char str[1024];
@@ -585,7 +596,7 @@ autorel_tls(void)
}
-static void
+static void HEIM_CALLCONV
autorel_dealloc(void *ptr)
{
heim_auto_release_t ar = ptr;
@@ -614,10 +625,10 @@ autorel_cmp(void *a, void *b)
return (a == b);
}
-static unsigned long
+static uintptr_t
autorel_hash(void *ptr)
{
- return (unsigned long)ptr;
+ return (uintptr_t)ptr;
}
@@ -764,9 +775,10 @@ heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key,
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) {
+ } else {
int idx = -1;
+ /* node_type == HEIM_TID_ARRAY */
if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
idx = heim_number_get_int(path_element);
if (idx < 0) {
@@ -778,12 +790,6 @@ heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key,
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;
}
@@ -919,14 +925,14 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
heim_abort("heim_path_vcreate() does not create root nodes");
while (path_element != NULL) {
+ int idx = -1;
+
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) {
@@ -937,10 +943,16 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
"and positive");
return EINVAL;
}
- if (idx < heim_array_get_length(node))
+ if (idx < heim_array_get_length(node)) {
next_node = heim_array_get_value(node, idx);
- else
+ } else if (idx == heim_array_get_length(node)) {
next_node = NULL;
+ } else {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "Index for array in path is too large");
+ return EINVAL;
+ }
} else if (node_type == HEIM_TID_DB && next_path_element != NULL) {
if (error)
*error = heim_error_create(EINVAL, "Interior node is a DB");
@@ -952,26 +964,31 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
/* Create missing interior node */
if (next_node == NULL) {
- next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */
- if (next_node == NULL) {
+ heim_dict_t new_node;
+
+ new_node = heim_dict_create(size); /* no arrays or DBs, just dicts */
+ if (new_node == NULL) {
ret = ENOMEM;
goto err;
}
if (node_type == HEIM_TID_DICT) {
- ret = heim_dict_set_value(node, path_element, next_node);
+ ret = heim_dict_set_value(node, path_element, new_node);
+ next_node = heim_dict_get_value(node, path_element);
} 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);
+ new_node);
+ next_node = heim_array_get_value(node, idx);
} else {
ret = EINVAL;
if (error)
*error = heim_error_create(ret, "Node in path not a "
"container");
}
- heim_release(next_node);
+
+ heim_release(new_node);
if (ret)
goto err;
}
diff --git a/lib/base/heimbase.h b/lib/base/heimbase.h
index 157cbd4c1105..4546df94699c 100644
--- a/lib/base/heimbase.h
+++ b/lib/base/heimbase.h
@@ -37,11 +37,17 @@
#define HEIM_BASE_H 1
#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
#if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
#include <pthread.h>
#endif
#include <krb5-types.h>
#include <stdarg.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#else
@@ -53,8 +59,101 @@
#endif
#endif
+#include <stdint.h>
+
+#include <heim_err.h>
+
+#ifdef _WIN32
+#define HEIM_CALLCONV __stdcall
+#define HEIM_LIB_CALL __stdcall
+#else
+#define HEIM_CALLCONV
+#define HEIM_LIB_CALL
+#endif
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
#define HEIM_BASE_API_VERSION 20130210
+/*
+ * Generic facilities (moved from lib/krb5/.
+ */
+
+typedef int32_t heim_error_code;
+typedef struct heim_context_s *heim_context;
+typedef struct heim_pcontext_s *heim_pcontext;
+
+typedef void (HEIM_CALLCONV *heim_log_log_func_t)(heim_context,
+ const char *,
+ const char *,
+ void *);
+typedef void (HEIM_CALLCONV *heim_log_close_func_t)(void *);
+
+typedef struct heim_log_facility_s heim_log_facility;
+
+typedef uintptr_t
+(HEIM_LIB_CALL *heim_get_instance_func_t)(const char *);
+
+#define HEIM_PLUGIN_INVOKE_ALL 1
+
+struct heim_plugin_data {
+ const char *module;
+ const char *name;
+ int min_version;
+ const char *const *deps;
+ heim_get_instance_func_t get_instance;
+};
+
+/*
+ * heim_config_binding is identical to struct krb5_config_binding
+ * within krb5.h. Its format is public and used by callers of
+ * krb5_config_get_list() and krb5_config_vget_list().
+ */
+enum heim_config_type {
+ heim_config_string,
+ heim_config_list,
+};
+struct heim_config_binding {
+ enum heim_config_type type;
+ char *name;
+ struct heim_config_binding *next;
+ union {
+ char *string;
+ struct heim_config_binding *list;
+ void *generic;
+ } u;
+};
+typedef struct heim_config_binding heim_config_binding;
+typedef struct heim_config_binding heim_config_section;
+
+/*
+ * CF-like, JSON APIs
+ */
+
+typedef enum heim_tid_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_PA_AUTH_MECH = 136,
+ HEIM_TID_PAC = 137,
+ HEIM_TID_USER = 255
+} heim_tid;
+
typedef void * heim_object_t;
typedef unsigned int heim_tid_t;
typedef heim_object_t heim_bool_t;
@@ -89,37 +188,7 @@ typedef long heim_base_once_t; /* XXX arch dependant */
#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));
+typedef void (HEIM_CALLCONV *heim_type_dealloc)(void *);
#define heim_assert(e,t) \
(heim_builtin_expect(!(e), 0) ? heim_abort(t ":" #e) : (void)0)
@@ -128,70 +197,23 @@ heim_abortv(const char *fmt, va_list ap)
*
*/
-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
*/
@@ -199,15 +221,6 @@ void heim_dict_delete_key(heim_dict_t, heim_object_t);
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 *);
@@ -217,41 +230,10 @@ heim_string_t __heim_string_constant(const char *);
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)
*/
@@ -268,14 +250,6 @@ typedef struct heim_base_data heim_octet_string;
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
*/
@@ -325,52 +299,18 @@ 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);
-
/*
- *
+ * Autorelease
*/
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
*/
@@ -383,25 +323,19 @@ typedef enum heim_json_flags {
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_F_ONE_LINE = 128,
+ HEIM_JSON_F_ESCAPE_NON_ASCII = 256,
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII = 512,
+ /* The default is to indent with one tab */
+ HEIM_JSON_F_INDENT2 = 1024,
+ HEIM_JSON_F_INDENT4 = 2048,
+ HEIM_JSON_F_INDENT8 = 4096,
} 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.
*
@@ -422,10 +356,19 @@ 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 *);
+#include <heim_threads.h>
+#include <com_err.h>
+
+/*
+ * Service logging facility (moved from kdc/).
+ */
+
+#define HEIM_SVC_AUDIT_EATWHITE 0x1
+#define HEIM_SVC_AUDIT_VIS 0x2
+#define HEIM_SVC_AUDIT_VISLAST 0x4
+
+typedef struct heim_svc_req_desc_common_s *heim_svc_req_desc;
+
+#include <heimbase-protos.h>
#endif /* HEIM_BASE_H */
diff --git a/lib/base/heimbasepriv.h b/lib/base/heimbasepriv.h
index eb155006fe0d..a431da478ad4 100644
--- a/lib/base/heimbasepriv.h
+++ b/lib/base/heimbasepriv.h
@@ -42,31 +42,11 @@
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 uintptr_t (*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
-
-};
+typedef const struct heim_type_data *heim_const_type_t;
struct heim_type_data {
heim_tid_t tid;
@@ -79,7 +59,7 @@ struct heim_type_data {
heim_type_description desc;
};
-heim_type_t _heim_get_isa(heim_object_t);
+heim_const_type_t _heim_get_isa(heim_object_t);
heim_type_t
_heim_create_type(const char *name,
@@ -91,7 +71,7 @@ _heim_create_type(const char *name,
heim_type_description desc);
heim_object_t
-_heim_alloc_object(heim_type_t type, size_t size);
+_heim_alloc_object(heim_const_type_t type, size_t size);
void *
_heim_get_isaextra(heim_object_t o, size_t idx);
diff --git a/lib/base/heimqueue.h b/lib/base/heimqueue.h
deleted file mode 100644
index 423a68478792..000000000000
--- a/lib/base/heimqueue.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/* $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
index 2ef371b975ea..4fa0f2d5aff0 100644
--- a/lib/base/json.c
+++ b/lib/base/json.c
@@ -37,10 +37,12 @@
#include <ctype.h>
#include <base64.h>
+#ifndef WIN32
+#include <langinfo.h>
+#endif
+
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)
@@ -66,7 +68,7 @@ struct heim_strbuf {
};
static int
-base2json(heim_object_t, struct twojson *);
+base2json(heim_object_t, struct twojson *, int);
static void
indent(struct twojson *j)
@@ -74,8 +76,18 @@ 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");
+ if (j->flags & HEIM_JSON_F_INDENT2)
+ while (i--)
+ j->out(j->ctx, " ");
+ else if (j->flags & HEIM_JSON_F_INDENT4)
+ while (i--)
+ j->out(j->ctx, " ");
+ else if (j->flags & HEIM_JSON_F_INDENT8)
+ while (i--)
+ j->out(j->ctx, " ");
+ else
+ while (i--)
+ j->out(j->ctx, "\t");
}
static void
@@ -90,7 +102,7 @@ array2json(heim_object_t value, void *ctx, int *stop)
j->out(j->ctx, NULL); /* eat previous '\n' if possible */
j->out(j->ctx, ",\n");
}
- j->ret = base2json(value, j);
+ j->ret = base2json(value, j, 0);
}
static void
@@ -105,19 +117,77 @@ dict2json(heim_object_t key, heim_object_t value, void *ctx)
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);
+ j->ret = base2json(key, j, 0);
if (j->ret)
return;
- j->indent--;
+ switch (heim_get_tid(value)) {
+ case HEIM_TID_ARRAY:
+ case HEIM_TID_DICT:
+ case HEIM_TID_DATA:
+ j->out(j->ctx, ":\n");
+ j->indent++;
+ j->ret = base2json(value, j, 0);
+ if (j->ret)
+ return;
+ j->indent--;
+ break;
+ default:
+ j->out(j->ctx, ": ");
+ j->ret = base2json(value, j, 1);
+ break;
+ }
+}
+
+#ifndef WIN32
+static void
+init_is_utf8(void *ptr)
+{
+ *(int *)ptr = strcasecmp("utf-8", nl_langinfo(CODESET)) == 0;
+}
+#endif
+
+int
+heim_locale_is_utf8(void)
+{
+#ifdef WIN32
+ return 0; /* XXX Implement */
+#else
+ static int locale_is_utf8 = -1;
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
+
+ heim_base_once_f(&once, &locale_is_utf8, init_is_utf8);
+ return locale_is_utf8;
+#endif
+}
+
+static void
+out_escaped_bmp(struct twojson *j, const unsigned char *p, int nbytes)
+{
+ unsigned char e[sizeof("\\u0000")];
+ unsigned codepoint;
+
+ if (nbytes == 2)
+ codepoint = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f);
+ else if (nbytes == 3)
+ codepoint = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
+ else
+ abort();
+ e[0] = '\\';
+ e[1] = 'u';
+ e[2] = codepoint >> 12;
+ e[2] += (e[2] < 10) ? '0' : ('A' - 10);
+ e[3] = (codepoint >> 8) & 0x0f;
+ e[3] += (e[3] < 10) ? '0' : ('A' - 10);
+ e[4] = (codepoint >> 4) & 0x0f;
+ e[4] += (e[4] < 10) ? '0' : ('A' - 10);
+ e[5] = codepoint & 0x0f;
+ e[5] += (e[5] < 10) ? '0' : ('A' - 10);
+ e[6] = '\0';
+ j->out(j->ctx, (char *)e);
}
static int
-base2json(heim_object_t obj, struct twojson *j)
+base2json(heim_object_t obj, struct twojson *j, int skip_indent)
{
heim_tid_t type;
int first = 0;
@@ -166,12 +236,186 @@ base2json(heim_object_t obj, struct twojson *j)
j->first = first;
break;
- case HEIM_TID_STRING:
- indent(j);
+ case HEIM_TID_STRING: {
+ const unsigned char *s = (const unsigned char *)heim_string_get_utf8(obj);
+ const unsigned char *p;
+ unsigned int c, cp, ctop, cbot;
+ char e[sizeof("\\u0123\\u3210")];
+ int good;
+ size_t i;
+
+ if (!skip_indent)
+ indent(j);
j->out(j->ctx, "\"");
- j->out(j->ctx, heim_string_get_utf8(obj));
+ for (p = s; (c = *p); p++) {
+ switch (c) {
+ /* ASCII control characters w/ C-like escapes */
+ case '\b': j->out(j->ctx, "\\b"); continue;
+ case '\f': j->out(j->ctx, "\\f"); continue;
+ case '\n': j->out(j->ctx, "\\n"); continue;
+ case '\r': j->out(j->ctx, "\\r"); continue;
+ case '\t': j->out(j->ctx, "\\t"); continue;
+ /* Other must-escape non-control ASCII characters */
+ case '"': j->out(j->ctx, "\\\""); continue;
+ case '\\': j->out(j->ctx, "\\\\"); continue;
+ default: break;
+ }
+
+ /*
+ * JSON string encoding is... complex.
+ *
+ * Invalid UTF-8 w/ HEIM_JSON_F_STRICT_STRINGS set -> return 1
+ *
+ * Invalid UTF-8 w/o HEIM_JSON_F_STRICT_STRINGS set -> pass
+ * through, a sort of Heimdal WTF-8, but not _the_ WTF-8.
+ */
+ if (c < 0x20) {
+ /* ASCII control character w/o C-like escape */
+ e[0] = '\\';
+ e[1] = 'u';
+ e[2] = '0';
+ e[3] = '0';
+ e[4] = "0123456789ABCDEF"[c>>4];
+ e[5] = "0123456789ABCDEF"[c & 0x0f];
+ e[6] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ if (c < 0x80) {
+ /* ASCII */
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ if ((c & 0xc0) == 0x80) {
+ /* UTF-8 bare non-leading byte */
+ if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ return 1;
+ }
+ if ((c & 0xe0) == 0xc0) {
+ /* UTF-8 leading byte of two-byte sequence */
+ good = 1;
+ for (i = 1; i < 2 && good && p[i]; i++) {
+ if ((p[i] & 0xc0) != 0x80)
+ good = 0;
+ }
+ if (i != 2)
+ good = 0;
+ if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ } else if (!good) {
+ return 1;
+ }
+ if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) {
+ out_escaped_bmp(j, p, 2);
+ p += 1;
+ continue;
+ }
+ e[0] = c;
+ e[1] = p[1];
+ e[2] = '\0';
+ j->out(j->ctx, e);
+ p += 1;
+ continue;
+ }
+ if ((c & 0xf0) == 0xe0) {
+ /* UTF-8 leading byte of three-byte sequence */
+ good = 1;
+ for (i = 1; i < 3 && good && p[i]; i++) {
+ if ((p[i] & 0xc0) != 0x80)
+ good = 0;
+ }
+ if (i != 3)
+ good = 0;
+ if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ } else if (!good) {
+ return 1;
+ }
+ if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) {
+ out_escaped_bmp(j, p, 3);
+ p += 2;
+ continue;
+ }
+ e[0] = c;
+ e[1] = p[1];
+ e[2] = p[2];
+ e[3] = '\0';
+ j->out(j->ctx, e);
+ p += 2;
+ continue;
+ }
+
+ if (c > 0xf7) {
+ /* Invalid UTF-8 leading byte */
+ if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ return 1;
+ }
+
+ /*
+ * A codepoint > U+FFFF, needs encoding a la UTF-16 surrogate
+ * pair because JSON takes after JS which uses UTF-16. Ugly.
+ */
+ cp = c & 0x7;
+ good = 1;
+ for (i = 1; i < 4 && good && p[i]; i++) {
+ if ((p[i] & 0xc0) == 0x80)
+ cp = (cp << 6) | (p[i] & 0x3f);
+ else
+ good = 0;
+ }
+ if (i != 4)
+ good = 0;
+ if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ } else if (!good) {
+ return 1;
+ }
+ p += 3;
+
+ cp -= 0x10000;
+ ctop = 0xD800 + (cp >> 10);
+ cbot = 0xDC00 + (cp & 0x3ff);
+
+ e[0 ] = '\\';
+ e[1 ] = 'u';
+ e[2 ] = "0123456789ABCDEF"[(ctop ) >> 12];
+ e[3 ] = "0123456789ABCDEF"[(ctop & 0x0f00) >> 8];
+ e[4 ] = "0123456789ABCDEF"[(ctop & 0x00f0) >> 4];
+ e[5 ] = "0123456789ABCDEF"[(ctop & 0x000f) ];
+ e[6 ] = '\\';
+ e[7 ] = 'u';
+ e[8 ] = "0123456789ABCDEF"[(cbot ) >> 12];
+ e[9 ] = "0123456789ABCDEF"[(cbot & 0x0f00) >> 8];
+ e[10] = "0123456789ABCDEF"[(cbot & 0x00f0) >> 4];
+ e[11] = "0123456789ABCDEF"[(cbot & 0x000f) ];
+ e[12] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
j->out(j->ctx, "\"");
break;
+ }
case HEIM_TID_DATA: {
heim_dict_t d;
@@ -220,7 +464,7 @@ base2json(heim_object_t obj, struct twojson *j)
heim_release(d);
return ENOMEM;
}
- ret = base2json(d, j);
+ ret = base2json(d, j, 0);
heim_release(d);
if (ret)
return ret;
@@ -230,17 +474,20 @@ base2json(heim_object_t obj, struct twojson *j)
case HEIM_TID_NUMBER: {
char num[32];
- indent(j);
+ if (!skip_indent)
+ indent(j);
snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
j->out(j->ctx, num);
break;
}
case HEIM_TID_NULL:
- indent(j);
+ if (!skip_indent)
+ indent(j);
j->out(j->ctx, "null");
break;
case HEIM_TID_BOOL:
- indent(j);
+ if (!skip_indent)
+ indent(j);
j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
break;
default:
@@ -255,9 +502,6 @@ heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
{
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;
@@ -267,7 +511,11 @@ heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
j.ret = 0;
j.first = 1;
- return base2json(obj, &j);
+ if (!(flags & HEIM_JSON_F_NO_ESCAPE_NON_ASCII) &&
+ !heim_locale_is_utf8())
+ j.flags |= HEIM_JSON_F_ESCAPE_NON_ASCII;
+
+ return base2json(obj, &j, 0);
}
@@ -342,93 +590,425 @@ parse_number(struct parse_ctx *ctx)
return heim_number_create(number * neg);
}
+/*
+ * Read 4 hex digits from ctx->p.
+ *
+ * If we don't have enough, rewind ctx->p and return -1 .
+ */
+static int
+unescape_unicode(struct parse_ctx *ctx)
+{
+ int c = 0;
+ int i;
+
+ for (i = 0; i < 4 && ctx->p < ctx->pend; i++, ctx->p++) {
+ if (*ctx->p >= '0' && *ctx->p <= '9') {
+ c = (c << 4) + (*ctx->p - '0');
+ } else if (*ctx->p >= 'A' && *ctx->p <= 'F') {
+ c = (c << 4) + (10 + *ctx->p - 'A');
+ } else if (*ctx->p >= 'a' && *ctx->p <= 'f') {
+ c = (c << 4) + (10 + *ctx->p - 'a');
+ } else {
+ ctx->p -= i;
+ return -1;
+ }
+ }
+ return c;
+}
+
+static int
+encode_utf8(struct parse_ctx *ctx, char **pp, char *pend, int c)
+{
+ char *p = *pp;
+
+ if (c < 0x80) {
+ /* ASCII */
+ if (p >= pend) return 0;
+ *(p++) = c;
+ *pp = p;
+ return 1;
+ }
+ if (c < 0x800) {
+ /* 2 code unit UTF-8 sequence */
+ if (p >= pend) return 0;
+ *(p++) = 0xc0 | ((c >> 6) );
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c ) & 0x3f);
+ *pp = p;
+ return 1;
+ }
+ if (c < 0x10000) {
+ /* 3 code unit UTF-8 sequence */
+ if (p >= pend) return 0;
+ *(p++) = 0xe0 | ((c >> 12) );
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c >> 6) & 0x3f);
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c) & 0x3f);
+ *pp = p;
+ return 1;
+ }
+ if (c < 0x110000) {
+ /* 4 code unit UTF-8 sequence */
+ if (p >= pend) return 0;
+ *(p++) = 0xf0 | ((c >> 18) );
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c >> 12) & 0x3f);
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c >> 6) & 0x3f);
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c) & 0x3f);
+ *pp = p;
+ return 1;
+ }
+ return 0;
+}
+
+static heim_string_t
+parse_string_error(struct parse_ctx *ctx,
+ char *freeme,
+ const char *msg)
+{
+ free(freeme);
+ ctx->error = heim_error_create(EINVAL, "%s at %lu", msg, ctx->lineno);
+ return NULL;
+}
+
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;
+ heim_object_t o;
+ size_t alloc_len = 0;
+ size_t need = 0;
+ char *p0, *p, *pend;
+ int strict = ctx->flags & HEIM_JSON_F_STRICT_STRINGS;
+ int binary = 0;
+
+ if (*ctx->p != '"')
+ return parse_string_error(ctx, NULL,
+ "Expected a JSON string but found "
+ "something else");
+ start = ++(ctx->p);
+
+ /* Estimate how many bytes we need to allocate */
+ p0 = p = pend = NULL;
+ for (need = 1; ctx->p < ctx->pend; ctx->p++) {
+ need++;
+ if (*ctx->p == '\\')
+ ctx->p++;
+ else if (*ctx->p == '"')
+ break;
}
+ if (ctx->p == ctx->pend)
+ return parse_string_error(ctx, NULL, "Unterminated JSON string");
- if (*ctx->p != '"') {
- ctx->error = heim_error_create(EINVAL, "Expected a JSON string but "
- "found something else at line %lu",
- ctx->lineno);
- return NULL;
+ ctx->p = start;
+ while (ctx->p < ctx->pend) {
+ const unsigned char *p_save;
+ int32_t ctop, cbot;
+
+ if (*ctx->p == '"') {
+ ctx->p++;
+ break;
+ }
+
+ /* Allocate or resize our output buffer if need be */
+ if (need || p == pend) {
+ char *tmp;
+
+ /*
+ * Work out how far p is into p0 to re-esablish p after
+ * the realloc()
+ */
+ size_t p0_to_p_len = (p - p0);
+
+ tmp = realloc(p0, alloc_len + need + 5 /* slop? */);
+
+ if (tmp == NULL) {
+ ctx->error = heim_error_create_enomem();
+ free(p0);
+ return NULL;
+ }
+ alloc_len += need + 5;
+
+ /*
+ * We have two pointers, p and p0, we want to keep them
+ * pointing into the same memory after the realloc()
+ */
+ p = tmp + p0_to_p_len;
+ p0 = tmp;
+ pend = p0 + alloc_len;
+
+ need = 0;
+ }
+
+ if (*ctx->p != '\\') {
+ unsigned char c = *ctx->p;
+
+ /*
+ * Not backslashed -> consume now.
+ *
+ * NOTE: All cases in this block must continue or return w/ error.
+ */
+
+ /* Check for unescaped ASCII control characters */
+ if (c == '\n') {
+ if (strict)
+ return parse_string_error(ctx, p0,
+ "Unescaped newline in JSON string");
+ /* Count the newline but don't add it to the decoding */
+ ctx->lineno++;
+ } else if (strict && *ctx->p <= 0x1f) {
+ return parse_string_error(ctx, p0, "Unescaped ASCII control character");
+ } else if (c == 0) {
+ binary = 1;
+ }
+ if (!strict || c < 0x80) {
+ /* ASCII, or not strict -> no need to validate */
+ *(p++) = c;
+ ctx->p++;
+ continue;
+ }
+
+ /*
+ * Being strict for parsing means we want to detect malformed UTF-8
+ * sequences.
+ *
+ * If not strict then we just go on below and add to `p' whatever
+ * bytes we find in `ctx->p' as we find them.
+ *
+ * For each two-byte sequence we need one more byte in `p[]'. For
+ * each three-byte sequence we need two more bytes in `p[]'.
+ *
+ * Setting `need' and looping will cause `p0' to be grown.
+ *
+ * NOTE: All cases in this block must continue or return w/ error.
+ */
+ if ((c & 0xe0) == 0xc0) {
+ /* Two-byte UTF-8 encoding */
+ if (pend - p < 2) {
+ need = 2;
+ continue; /* realloc p0 */
+ }
+
+ *(p++) = c;
+ ctx->p++;
+ if (ctx->p == ctx->pend)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ c = *(ctx->p++);
+ if ((c & 0xc0) != 0x80)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ *(p++) = c;
+ continue;
+ }
+ if ((c & 0xf0) == 0xe0) {
+ /* Three-byte UTF-8 encoding */
+ if (pend - p < 3) {
+ need = 3;
+ continue; /* realloc p0 */
+ }
+
+ *(p++) = c;
+ ctx->p++;
+ if (ctx->p == ctx->pend)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ c = *(ctx->p++);
+ if ((c & 0xc0) != 0x80)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ *(p++) = c;
+ c = *(ctx->p++);
+ if ((c & 0xc0) != 0x80)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ *(p++) = c;
+ continue;
+ }
+ if ((c & 0xf8) == 0xf0)
+ return parse_string_error(ctx, p0, "UTF-8 sequence not "
+ "encoded as escaped UTF-16");
+ if ((c & 0xc0) == 0x80)
+ return parse_string_error(ctx, p0,
+ "Invalid UTF-8 "
+ "(bare continuation code unit)");
+
+ return parse_string_error(ctx, p0, "Not UTF-8");
+ }
+
+ /* Backslash-quoted character */
+ ctx->p++;
+ if (ctx->p == ctx->pend) {
+ ctx->error =
+ heim_error_create(EINVAL,
+ "Unterminated JSON string at line %lu",
+ ctx->lineno);
+ free(p0);
+ return NULL;
+ }
+ switch (*ctx->p) {
+ /* Simple escapes */
+ case 'b': *(p++) = '\b'; ctx->p++; continue;
+ case 'f': *(p++) = '\f'; ctx->p++; continue;
+ case 'n': *(p++) = '\n'; ctx->p++; continue;
+ case 'r': *(p++) = '\r'; ctx->p++; continue;
+ case 't': *(p++) = '\t'; ctx->p++; continue;
+ case '"': *(p++) = '"'; ctx->p++; continue;
+ case '\\': *(p++) = '\\'; ctx->p++; continue;
+ /* Escaped Unicode handled below */
+ case 'u':
+ /*
+ * Worst case for !strict we need 11 bytes for a truncated non-BMP
+ * codepoint escape. Call it 12.
+ */
+ if (strict)
+ need = 4;
+ else
+ need = 12;
+ if (pend - p < need) {
+ /* Go back to the backslash, realloc, try again */
+ ctx->p--;
+ continue;
+ }
+
+ need = 0;
+ ctx->p++;
+ break;
+ default:
+ if (!strict) {
+ *(p++) = *ctx->p;
+ ctx->p++;
+ continue;
+ }
+ ctx->error =
+ heim_error_create(EINVAL,
+ "Invalid backslash escape at line %lu",
+ ctx->lineno);
+ free(p0);
+ return NULL;
+ }
+
+ /* Unicode code point */
+ if (pend - p < 12) {
+ need = 12;
+ ctx->p -= 2; /* for "\\u" */
+ continue; /* This will cause p0 to be realloc'ed */
+ }
+ p_save = ctx->p;
+ cbot = -3;
+ ctop = unescape_unicode(ctx);
+ if (ctop == -1 && strict)
+ return parse_string_error(ctx, p0, "Invalid escaped Unicode");
+ if (ctop == -1) {
+ /*
+ * Not strict; tolerate bad input.
+ *
+ * Output "\\u" and then loop to treat what we expected to be four
+ * digits as if they were not part of an escaped Unicode codepoint.
+ */
+ ctx->p = p_save;
+ if (p < pend)
+ *(p++) = '\\';
+ if (p < pend)
+ *(p++) = 'u';
+ continue;
+ }
+ if (ctop == 0) {
+ *(p++) = '\0';
+ binary = 1;
+ continue;
+ }
+ if (ctop < 0xd800) {
+ if (!encode_utf8(ctx, &p, pend, ctop))
+ return parse_string_error(ctx, p0,
+ "Internal JSON string parse error");
+ continue;
+ }
+
+ /*
+ * We parsed the top escaped codepoint of a surrogate pair encoding
+ * of a non-BMP Unicode codepoint. What follows must be another
+ * escaped codepoint.
+ */
+ if (ctx->p < ctx->pend && ctx->p[0] == '\\')
+ ctx->p++;
+ else
+ ctop = -1;
+ if (ctop > -1 && ctx->p < ctx->pend && ctx->p[0] == 'u')
+ ctx->p++;
+ else
+ ctop = -1;
+ if (ctop > -1) {
+ /* Parse the hex digits of the bottom half of the surrogate pair */
+ cbot = unescape_unicode(ctx);
+ if (cbot == -1 || cbot < 0xdc00)
+ ctop = -1;
+ }
+ if (ctop == -1) {
+ if (strict)
+ return parse_string_error(ctx, p0,
+ "Invalid surrogate pair");
+
+ /*
+ * Output "\\u", rewind, output the digits of `ctop'.
+ *
+ * When we get to what should have been the bottom half of the
+ * pair we'll necessarily fail to parse it as a normal escaped
+ * Unicode codepoint, and once again, rewind and output its digits.
+ */
+ if (p < pend)
+ *(p++) = '\\';
+ if (p < pend)
+ *(p++) = 'u';
+ ctx->p = p_save;
+ continue;
+ }
+
+ /* Finally decode the surrogate pair then encode as UTF-8 */
+ ctop -= 0xd800;
+ cbot -= 0xdc00;
+ if (!encode_utf8(ctx, &p, pend, 0x10000 + ((ctop << 10) | (cbot & 0x3ff))))
+ return parse_string_error(ctx, p0,
+ "Internal JSON string parse error");
}
- 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;
+ if (p0 == NULL)
+ return heim_string_create("");
- return o;
- }
- ctx->p += 1;
+ /* NUL-terminate for rk_base64_decode() and plain paranoia */
+ if (p0 != NULL && p == pend) {
+ /*
+ * Work out how far p is into p0 to re-esablish p after
+ * the realloc()
+ */
+ size_t p0_to_pend_len = (pend - p0);
+ char *tmp = realloc(p0, 1 + p0_to_pend_len);
+
+ if (tmp == NULL) {
+ ctx->error = heim_error_create_enomem();
+ free(p0);
+ return NULL;
+ }
+ /*
+ * We have three pointers, p, pend (which are the same)
+ * and p0, we want to keep them pointing into the same
+ * memory after the realloc()
+ */
+ p = tmp + p0_to_pend_len;
+
+ pend = p + 1;
+ p0 = tmp;
}
- out:
- ctx->error = heim_error_create(EINVAL, "ran out of string");
- return NULL;
+ *(p++) = '\0';
+
+ /* If there's embedded NULs, it's not a C string */
+ if (binary) {
+ o = heim_data_ref_create(p0, (p - 1) - p0, free);
+ return o;
+ }
+
+ /* Sadly this will copy `p0' */
+ o = heim_string_create_with_bytes(p0, p - p0);
+ free(p0);
+ return o;
}
static int
@@ -809,3 +1389,83 @@ heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_
}
return str;
}
+
+struct heim_eq_f_ctx {
+ heim_dict_t other;
+ int ret;
+};
+
+static void
+heim_eq_dict_iter_f(heim_object_t key, heim_object_t val, void *d)
+{
+ struct heim_eq_f_ctx *ctx = d;
+ heim_object_t other_val;
+
+ if (!ctx->ret)
+ return;
+
+ /*
+ * This doesn't work if the key is an array or a dict, which, anyways,
+ * isn't allowed in JSON, though we allow it.
+ */
+ other_val = heim_dict_get_value(ctx->other, key);
+ ctx->ret = heim_json_eq(val, other_val);
+}
+
+int
+heim_json_eq(heim_object_t a, heim_object_t b)
+{
+ heim_tid_t atid, btid;
+
+ if (a == b)
+ return 1;
+ if (a == NULL || b == NULL)
+ return 0;
+ atid = heim_get_tid(a);
+ btid = heim_get_tid(b);
+ if (atid != btid)
+ return 0;
+ switch (atid) {
+ case HEIM_TID_ARRAY: {
+ size_t len = heim_array_get_length(b);
+ size_t i;
+
+ if (heim_array_get_length(a) != len)
+ return 0;
+ for (i = 0; i < len; i++) {
+ if (!heim_json_eq(heim_array_get_value(a, i),
+ heim_array_get_value(b, i)))
+ return 0;
+ }
+ return 1;
+ }
+ case HEIM_TID_DICT: {
+ struct heim_eq_f_ctx ctx;
+
+ ctx.other = b;
+ ctx.ret = 1;
+ heim_dict_iterate_f(a, &ctx, heim_eq_dict_iter_f);
+
+ if (ctx.ret) {
+ ctx.other = a;
+ heim_dict_iterate_f(b, &ctx, heim_eq_dict_iter_f);
+ }
+ return ctx.ret;
+ }
+ case HEIM_TID_STRING:
+ return strcmp(heim_string_get_utf8(a), heim_string_get_utf8(b)) == 0;
+ case HEIM_TID_DATA: {
+ return heim_data_get_length(a) == heim_data_get_length(b) &&
+ memcmp(heim_data_get_ptr(a), heim_data_get_ptr(b),
+ heim_data_get_length(a)) == 0;
+ }
+ case HEIM_TID_NUMBER:
+ return heim_number_get_long(a) == heim_number_get_long(b);
+ case HEIM_TID_NULL:
+ case HEIM_TID_BOOL:
+ return heim_bool_val(a) == heim_bool_val(b);
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/lib/base/log.c b/lib/base/log.c
new file mode 100644
index 000000000000..9a97276e1534
--- /dev/null
+++ b/lib/base/log.c
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 1997-2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 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 "heim_threads.h"
+#include "heimbase-atomics.h"
+#include "heimbase.h"
+#include "heimbase-svc.h"
+#include <assert.h>
+#include <stdarg.h>
+#include <vis.h>
+#include <base64.h>
+
+struct heim_log_facility_internal {
+ int min;
+ int max;
+ heim_log_log_func_t log_func;
+ heim_log_close_func_t close_func;
+ void *data;
+};
+
+struct heim_log_facility_s {
+ char *program;
+ heim_base_atomic(uint32_t) refs;
+ size_t len;
+ struct heim_log_facility_internal *val;
+};
+
+typedef struct heim_pcontext_s *heim_pcontext;
+typedef struct heim_pconfig *heim_pconfig;
+struct heim_svc_req_desc_common_s {
+ HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS;
+};
+
+static struct heim_log_facility_internal *
+log_realloc(heim_log_facility *f)
+{
+ struct heim_log_facility_internal *fp;
+ fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
+ if (fp == NULL)
+ return NULL;
+ f->len++;
+ f->val = fp;
+ fp += f->len - 1;
+ return fp;
+}
+
+struct s2i {
+ const char *s;
+ int val;
+};
+
+#define L(X) { #X, LOG_ ## X }
+
+static struct s2i syslogvals[] = {
+ L(EMERG),
+ L(ALERT),
+ L(CRIT),
+ L(ERR),
+ L(WARNING),
+ L(NOTICE),
+ L(INFO),
+ L(DEBUG),
+
+ L(AUTH),
+#ifdef LOG_AUTHPRIV
+ L(AUTHPRIV),
+#endif
+#ifdef LOG_CRON
+ L(CRON),
+#endif
+ L(DAEMON),
+#ifdef LOG_FTP
+ L(FTP),
+#endif
+ L(KERN),
+ L(LPR),
+ L(MAIL),
+#ifdef LOG_NEWS
+ L(NEWS),
+#endif
+ L(SYSLOG),
+ L(USER),
+#ifdef LOG_UUCP
+ L(UUCP),
+#endif
+ L(LOCAL0),
+ L(LOCAL1),
+ L(LOCAL2),
+ L(LOCAL3),
+ L(LOCAL4),
+ L(LOCAL5),
+ L(LOCAL6),
+ L(LOCAL7),
+ { NULL, -1 }
+};
+
+static int
+find_value(const char *s, struct s2i *table)
+{
+ while (table->s && strcasecmp(table->s, s) != 0)
+ table++;
+ return table->val;
+}
+
+heim_error_code
+heim_initlog(heim_context context,
+ const char *program,
+ heim_log_facility **fac)
+{
+ heim_log_facility *f = calloc(1, sizeof(*f));
+ if (f == NULL)
+ return heim_enomem(context);
+ f->refs = 1;
+ f->program = strdup(program);
+ if (f->program == NULL) {
+ free(f);
+ return heim_enomem(context);
+ }
+ *fac = f;
+ return 0;
+}
+
+heim_log_facility *
+heim_log_ref(heim_log_facility *fac)
+{
+ if (fac)
+ (void) heim_base_atomic_inc_32(&fac->refs);
+ return fac;
+}
+
+heim_error_code
+heim_addlog_func(heim_context context,
+ heim_log_facility *fac,
+ int min,
+ int max,
+ heim_log_log_func_t log_func,
+ heim_log_close_func_t close_func,
+ void *data)
+{
+ struct heim_log_facility_internal *fp = log_realloc(fac);
+ if (fp == NULL)
+ return heim_enomem(context);
+ fp->min = min;
+ fp->max = max;
+ fp->log_func = log_func;
+ fp->close_func = close_func;
+ fp->data = data;
+ return 0;
+}
+
+
+struct _heimdal_syslog_data{
+ int priority;
+};
+
+static void HEIM_CALLCONV
+log_syslog(heim_context context, const char *timestr,
+ const char *msg, void *data)
+{
+ struct _heimdal_syslog_data *s = data;
+ syslog(s->priority, "%s", msg);
+}
+
+static void HEIM_CALLCONV
+close_syslog(void *data)
+{
+ free(data);
+ closelog();
+}
+
+static heim_error_code
+open_syslog(heim_context context,
+ heim_log_facility *facility, int min, int max,
+ const char *sev, const char *fac)
+{
+ struct _heimdal_syslog_data *sd;
+ heim_error_code ret;
+ int i;
+
+ if (facility == NULL)
+ return EINVAL;
+ if ((sd = calloc(1, sizeof(*sd))) == NULL)
+ return heim_enomem(context);
+ i = find_value(sev, syslogvals);
+ if (i == -1)
+ i = LOG_ERR;
+ sd->priority = i;
+ i = find_value(fac, syslogvals);
+ if (i == -1)
+ i = LOG_AUTH;
+ sd->priority |= i;
+ roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
+ ret = heim_addlog_func(context, facility, min, max, log_syslog,
+ close_syslog, sd);
+ if (ret)
+ free(sd);
+ else
+ sd = NULL;
+ return ret;
+}
+
+struct file_data {
+ char *filename;
+ const char *mode;
+ struct timeval tv;
+ FILE *fd;
+ int disp;
+#define FILEDISP_KEEPOPEN 0x1
+#define FILEDISP_REOPEN 0x2
+#define FILEDISP_IFEXISTS 0x4
+};
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+static void HEIM_CALLCONV
+log_file(heim_context context, const char *timestr, const char *msg, void *data)
+{
+ struct timeval tv;
+ struct file_data *f = data;
+ FILE *logf = f->fd;
+ char *msgclean;
+ size_t i = 0;
+ size_t j;
+
+ if (f->filename && (logf == NULL || (f->disp & FILEDISP_REOPEN))) {
+ int flags = O_WRONLY|O_APPEND;
+ int fd;
+
+ if (f->mode[0] == 'e') {
+ flags |= O_CLOEXEC;
+ i = 1;
+ }
+ if (f->mode[i] == 'w')
+ flags |= O_TRUNC;
+ if (f->mode[i + 1] == '+')
+ flags |= O_RDWR;
+
+ if (f->disp & FILEDISP_IFEXISTS) {
+ /* Cache failure for 1s */
+ gettimeofday(&tv, NULL);
+ if (tv.tv_sec == f->tv.tv_sec)
+ return;
+ } else {
+ flags |= O_CREAT;
+ }
+
+ fd = open(f->filename, flags, 0666); /* umask best be set */
+ if (fd == -1) {
+ if (f->disp & FILEDISP_IFEXISTS)
+ gettimeofday(&f->tv, NULL);
+ return;
+ }
+ rk_cloexec(fd);
+ logf = fdopen(fd, f->mode);
+ }
+ if (f->fd == NULL && (f->disp & FILEDISP_KEEPOPEN))
+ f->fd = logf;
+ if (logf == NULL)
+ return;
+ /*
+ * make sure the log doesn't contain special chars:
+ * we used to use strvisx(3) to encode the log, but this is
+ * inconsistent with our syslog(3) code which does not do this.
+ * It also makes it inelegant to write data which has already
+ * been quoted such as what krb5_unparse_principal() gives us.
+ * So, we change here to eat the special characters, instead.
+ */
+ if (msg && (msgclean = strdup(msg))) {
+ for (i = 0, j = 0; msg[i]; i++)
+ if (msg[i] >= 32 || msg[i] == '\t')
+ msgclean[j++] = msg[i];
+ fprintf(logf, "%s %s\n", timestr ? timestr : "", msgclean);
+ free(msgclean);
+ }
+ if (logf != f->fd)
+ fclose(logf);
+}
+
+static void HEIM_CALLCONV
+close_file(void *data)
+{
+ struct file_data *f = data;
+ if (f->fd && f->fd != stdout && f->fd != stderr)
+ fclose(f->fd);
+ free(f->filename);
+ free(data);
+}
+
+static heim_error_code
+open_file(heim_context context, heim_log_facility *fac, int min, int max,
+ const char *filename, const char *mode, FILE *f, int disp,
+ int exp_tokens)
+{
+ heim_error_code ret = 0;
+ struct file_data *fd;
+
+ if ((fd = calloc(1, sizeof(*fd))) == NULL)
+ return heim_enomem(context);
+
+ fd->filename = NULL;
+ fd->mode = mode;
+ fd->fd = f;
+ fd->disp = disp;
+
+ if (filename) {
+ if (exp_tokens)
+ ret = heim_expand_path_tokens(context, filename, 1, &fd->filename, NULL);
+ else if ((fd->filename = strdup(filename)) == NULL)
+ ret = heim_enomem(context);
+ }
+ if (ret == 0)
+ ret = heim_addlog_func(context, fac, min, max, log_file, close_file, fd);
+ if (ret) {
+ free(fd->filename);
+ free(fd);
+ } else if (disp & FILEDISP_KEEPOPEN) {
+ log_file(context, NULL, NULL, fd);
+ fd = NULL;
+ }
+ return ret;
+}
+
+heim_error_code
+heim_addlog_dest(heim_context context, heim_log_facility *f, const char *orig)
+{
+ heim_error_code ret = 0;
+ int min = 0, max = 3, n;
+ char c;
+ const char *p = orig;
+#ifdef _WIN32
+ const char *q;
+#endif
+
+ n = sscanf(p, "%d%c%d/", &min, &c, &max);
+ if (n == 2) {
+ if (ISPATHSEP(c)) {
+ if (min < 0) {
+ max = -min;
+ min = 0;
+ } else {
+ max = min;
+ }
+ }
+ if (c == '-')
+ max = -1;
+ }
+ if (n) {
+#ifdef _WIN32
+ q = strrchr(p, '\\');
+ if (q != NULL)
+ p = q;
+ else
+#endif
+ p = strchr(p, '/');
+ if (p == NULL) {
+ heim_set_error_message(context, EINVAL /*XXX HEIM_ERR_LOG_PARSE*/,
+ N_("failed to parse \"%s\"", ""), orig);
+ return EINVAL /*XXX HEIM_ERR_LOG_PARSE*/;
+ }
+ p++;
+ }
+ if (strcmp(p, "STDERR") == 0) {
+ ret = open_file(context, f, min, max, NULL, "a", stderr,
+ FILEDISP_KEEPOPEN, 0);
+ } else if (strcmp(p, "CONSOLE") == 0) {
+ /* XXX WIN32 */
+ ret = open_file(context, f, min, max, "/dev/console", "w", NULL,
+ FILEDISP_KEEPOPEN, 0);
+ } else if (strncmp(p, "EFILE:", 5) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("EFILE:") - 1, "a",
+ NULL, FILEDISP_IFEXISTS | FILEDISP_REOPEN, 1);
+ } else if (strncmp(p, "EFILE=", 5) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("EFILE=") - 1, "a",
+ NULL, FILEDISP_IFEXISTS | FILEDISP_KEEPOPEN, 1);
+ } else if (strncmp(p, "FILE:", sizeof("FILE:") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("FILE:") - 1, "a",
+ NULL, FILEDISP_REOPEN, 1);
+ } else if (strncmp(p, "FILE=", sizeof("FILE=") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("FILE=") - 1, "a",
+ NULL, FILEDISP_KEEPOPEN, 1);
+ } else if (strncmp(p, "DEVICE:", sizeof("DEVICE:") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("DEVICE:") - 1, "a",
+ NULL, FILEDISP_REOPEN, 0);
+ } else if (strncmp(p, "DEVICE=", sizeof("DEVICE=") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("DEVICE=") - 1, "a",
+ NULL, FILEDISP_KEEPOPEN, 0);
+ } else if (strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')) {
+ char severity[128] = "";
+ char facility[128] = "";
+ p += 6;
+ if (*p != '\0')
+ p++;
+ if (strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
+ strsep_copy(&p, ":", facility, sizeof(facility));
+ if (*severity == '\0')
+ strlcpy(severity, "ERR", sizeof(severity));
+ if (*facility == '\0')
+ strlcpy(facility, "AUTH", sizeof(facility));
+ ret = open_syslog(context, f, min, max, severity, facility);
+ } else {
+ ret = EINVAL; /*XXX HEIM_ERR_LOG_PARSE*/
+ heim_set_error_message(context, ret,
+ N_("unknown log type: %s", ""), p);
+ }
+ return ret;
+}
+
+heim_error_code
+heim_openlog(heim_context context,
+ const char *program,
+ const char **specs,
+ heim_log_facility **fac)
+{
+ heim_error_code ret;
+
+ ret = heim_initlog(context, program, fac);
+ if (ret)
+ return ret;
+
+ if (specs) {
+ size_t i;
+ for (i = 0; specs[i] && ret == 0; i++)
+ ret = heim_addlog_dest(context, *fac, specs[i]);
+ } else {
+ ret = heim_addlog_dest(context, *fac, "SYSLOG");
+ }
+ return ret;
+}
+
+void
+heim_closelog(heim_context context, heim_log_facility *fac)
+{
+ int i;
+
+ if (!fac || heim_base_atomic_dec_32(&fac->refs))
+ return;
+ for (i = 0; i < fac->len; i++)
+ (*fac->val[i].close_func)(fac->val[i].data);
+ free(fac->val);
+ free(fac->program);
+ fac->val = NULL;
+ fac->len = 0;
+ fac->program = NULL;
+ free(fac);
+ return;
+}
+
+static void
+format_time(heim_context context, time_t t, char *s, size_t len)
+{
+ struct tm *tm = heim_context_get_log_utc(context) ?
+ gmtime(&t) : localtime(&t);
+ if (tm && strftime(s, len, heim_context_get_time_fmt(context), tm))
+ return;
+ snprintf(s, len, "%ld", (long)t);
+}
+
+#undef __attribute__
+#define __attribute__(X)
+
+heim_error_code
+heim_vlog_msg(heim_context context,
+ heim_log_facility *fac,
+ char **reply,
+ int level,
+ const char *fmt,
+ va_list ap)
+__attribute__ ((__format__ (__printf__, 5, 0)))
+{
+
+ char *msg = NULL;
+ const char *actual = NULL;
+ char buf[64];
+ time_t t = 0;
+ int i;
+
+ if (!fac)
+ fac = context->log_dest;
+ for (i = 0; fac && i < fac->len; i++)
+ if (fac->val[i].min <= level &&
+ (fac->val[i].max < 0 || fac->val[i].max >= level)) {
+ if (t == 0) {
+ t = time(NULL);
+ format_time(context, t, buf, sizeof(buf));
+ }
+ if (actual == NULL) {
+ int ret = vasprintf(&msg, fmt, ap);
+ if (ret < 0 || msg == NULL)
+ actual = fmt;
+ else
+ actual = msg;
+ }
+ (*fac->val[i].log_func)(context, buf, actual, fac->val[i].data);
+ }
+ if (reply == NULL)
+ free(msg);
+ else
+ *reply = msg;
+ return 0;
+}
+
+heim_error_code
+heim_vlog(heim_context context,
+ heim_log_facility *fac,
+ int level,
+ const char *fmt,
+ va_list ap)
+__attribute__ ((__format__ (__printf__, 4, 0)))
+{
+ return heim_vlog_msg(context, fac, NULL, level, fmt, ap);
+}
+
+heim_error_code
+heim_log_msg(heim_context context,
+ heim_log_facility *fac,
+ int level,
+ char **reply,
+ const char *fmt,
+ ...)
+__attribute__ ((__format__ (__printf__, 5, 6)))
+{
+ va_list ap;
+ heim_error_code ret;
+
+ va_start(ap, fmt);
+ ret = heim_vlog_msg(context, fac, reply, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+heim_error_code
+heim_log(heim_context context,
+ heim_log_facility *fac,
+ int level,
+ const char *fmt,
+ ...)
+__attribute__ ((__format__ (__printf__, 4, 5)))
+{
+ va_list ap;
+ heim_error_code ret;
+
+ va_start(ap, fmt);
+ ret = heim_vlog(context, fac, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+void
+heim_debug(heim_context context,
+ int level,
+ const char *fmt,
+ ...)
+__attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ heim_log_facility *fac;
+ va_list ap;
+
+ if (context == NULL ||
+ (fac = heim_get_debug_dest(context)) == NULL)
+ return;
+
+ va_start(ap, fmt);
+ heim_vlog(context, fac, level, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_vdebug(heim_context context,
+ int level,
+ const char *fmt,
+ va_list ap)
+__attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ heim_log_facility *fac;
+
+ if (context == NULL ||
+ (fac = heim_get_debug_dest(context)) == NULL)
+ return;
+
+ heim_vlog(context, fac, level, fmt, ap);
+}
+
+heim_error_code
+heim_have_debug(heim_context context, int level)
+{
+ return (context != NULL && heim_get_debug_dest(context) != NULL);
+}
+
+heim_error_code
+heim_add_warn_dest(heim_context context, const char *program,
+ const char *log_spec)
+{
+ heim_log_facility *fac;
+
+ heim_error_code ret;
+
+ if ((fac = heim_get_warn_dest(context)) == NULL) {
+ ret = heim_initlog(context, program, &fac);
+ if (ret)
+ return ret;
+ heim_set_warn_dest(context, fac);
+ }
+
+ ret = heim_addlog_dest(context, fac, log_spec);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+heim_error_code
+heim_add_debug_dest(heim_context context, const char *program,
+ const char *log_spec)
+{
+ heim_log_facility *fac;
+ heim_error_code ret;
+
+ if ((fac = heim_get_debug_dest(context)) == NULL) {
+ ret = heim_initlog(context, program, &fac);
+ if (ret)
+ return ret;
+ heim_set_debug_dest(context, fac);
+ }
+
+ ret = heim_addlog_dest(context, fac, log_spec);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+struct heim_audit_kv_tuple {
+ heim_string_t key;
+ heim_object_t value;
+};
+
+static struct heim_audit_kv_tuple zero_tuple;
+
+static struct heim_audit_kv_tuple
+fmtkv(int flags, const char *k, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ size_t i;
+ ssize_t j;
+ struct heim_audit_kv_tuple kv;
+ char *value;
+ char *value_vis;
+
+ j = vasprintf(&value, fmt, ap);
+ if (j < 0 || value == NULL)
+ return zero_tuple;
+
+ /* We optionally eat the whitespace. */
+
+ if (flags & HEIM_SVC_AUDIT_EATWHITE) {
+ for (i=0, j=0; value[i]; i++)
+ if (value[i] != ' ' && value[i] != '\t')
+ value[j++] = value[i];
+ value[j] = '\0';
+ }
+
+ if (flags & (HEIM_SVC_AUDIT_VIS | HEIM_SVC_AUDIT_VISLAST)) {
+ int vis_flags = VIS_CSTYLE | VIS_OCTAL | VIS_NL;
+
+ if (flags & HEIM_SVC_AUDIT_VIS)
+ vis_flags |= VIS_WHITE;
+ value_vis = malloc((j + 1) * 4 + 1);
+ if (value_vis)
+ strvisx(value_vis, value, j, vis_flags);
+ free(value);
+ if (value_vis == NULL)
+ return zero_tuple;
+ } else
+ value_vis = value;
+
+ if (k)
+ kv.key = heim_string_create(k);
+ else
+ kv.key = NULL;
+ kv.value = heim_string_ref_create(value_vis, free);
+
+ return kv;
+}
+
+void
+heim_audit_vaddreason(heim_svc_req_desc r, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 2, 0)))
+{
+ struct heim_audit_kv_tuple kv;
+
+ kv = fmtkv(HEIM_SVC_AUDIT_VISLAST, NULL, fmt, ap);
+ if (kv.value == NULL) {
+ heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddreason: "
+ "failed to add reason (out of memory)");
+ return;
+ }
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddreason(): "
+ "adding reason %s", heim_string_get_utf8(kv.value));
+ if (r->reason) {
+ heim_string_t str2;
+
+ str2 = heim_string_create_with_format("%s: %s",
+ heim_string_get_utf8(kv.value),
+ heim_string_get_utf8(r->reason));
+ if (str2) {
+ heim_release(kv.value);
+ kv.value = str2;
+ }
+ }
+ heim_release(r->reason);
+ r->reason = kv.value;
+}
+
+void
+heim_audit_addreason(heim_svc_req_desc r, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ heim_audit_vaddreason(r, fmt, ap);
+ va_end(ap);
+}
+
+size_t
+addkv(heim_svc_req_desc r, heim_object_t key, heim_object_t value)
+{
+ size_t index;
+ heim_object_t obj;
+
+ obj = heim_dict_get_value(r->kv, key);
+ if (obj) {
+ if (heim_get_tid(obj) == HEIM_TID_ARRAY) {
+ index = heim_array_get_length(obj);
+ heim_array_append_value(obj, value);
+ } else {
+ heim_array_t array = heim_array_create();
+
+ index = 1;
+ heim_array_append_value(array, obj);
+ heim_array_append_value(array, value);
+ heim_dict_set_value(r->kv, key, array);
+ heim_release(array); /* retained by r->kv */
+ }
+ } else {
+ index = 0;
+ heim_dict_set_value(r->kv, key, value);
+ }
+
+ return index;
+}
+
+/*
+ * add a key-value token. if the key already exists, the value is
+ * promoted to an array of values.
+ */
+
+void
+heim_audit_vaddkv(heim_svc_req_desc r, int flags, const char *k,
+ const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 4, 0)))
+{
+ struct heim_audit_kv_tuple kv;
+ size_t index;
+
+ kv = fmtkv(flags, k, fmt, ap);
+ if (kv.key == NULL || kv.value == NULL) {
+ heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddkv: "
+ "failed to add kv pair (out of memory)");
+ heim_release(kv.key);
+ heim_release(kv.value);
+ return;
+ }
+
+ index = addkv(r, kv.key, kv.value);
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddkv(): "
+ "kv pair[%zu] %s=%s", index,
+ heim_string_get_utf8(kv.key), heim_string_get_utf8(kv.value));
+
+ heim_release(kv.key);
+ heim_release(kv.value);
+}
+
+void
+heim_audit_addkv(heim_svc_req_desc r, int flags, const char *k,
+ const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ heim_audit_vaddkv(r, flags, k, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_audit_addkv_timediff(heim_svc_req_desc r, const char *k,
+ const struct timeval *start,
+ const struct timeval *end)
+{
+ time_t sec;
+ int usec;
+ const char *sign = "";
+
+ if (end->tv_sec > start->tv_sec ||
+ (end->tv_sec == start->tv_sec && end->tv_usec >= start->tv_usec)) {
+ sec = end->tv_sec - start->tv_sec;
+ usec = end->tv_usec - start->tv_usec;
+ } else {
+ sec = start->tv_sec - end->tv_sec;
+ usec = start->tv_usec - end->tv_usec;
+ sign = "-";
+ }
+
+ if (usec < 0) {
+ usec += 1000000;
+ sec -= 1;
+ }
+
+ heim_audit_addkv(r, 0, k, "%s%ld.%06d", sign, (long)sec, usec);
+}
+
+void
+heim_audit_setkv_bool(heim_svc_req_desc r, const char *k, int v)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_number_t value;
+
+ if (key == NULL)
+ return;
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_bool(): "
+ "setting kv pair %s=%s", k, v ? "true" : "false");
+
+ value = heim_bool_create(v);
+ heim_dict_set_value(r->kv, key, value);
+ heim_release(key);
+ heim_release(value);
+}
+
+void
+heim_audit_addkv_number(heim_svc_req_desc r, const char *k, int64_t v)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_number_t value;
+
+ if (key == NULL)
+ return;
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_number(): "
+ "adding kv pair %s=%lld", k, (long long)v);
+
+ value = heim_number_create(v);
+ addkv(r, key, value);
+ heim_release(key);
+ heim_release(value);
+}
+
+void
+heim_audit_setkv_number(heim_svc_req_desc r, const char *k, int64_t v)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_number_t value;
+
+ if (key == NULL)
+ return;
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_number(): "
+ "setting kv pair %s=%lld", k, (long long)v);
+
+ value = heim_number_create(v);
+ heim_dict_set_value(r->kv, key, value);
+ heim_release(key);
+ heim_release(value);
+}
+
+void
+heim_audit_addkv_object(heim_svc_req_desc r, const char *k, heim_object_t value)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_string_t descr;
+
+ if (key == NULL)
+ return;
+
+ descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL);
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_object(): "
+ "adding kv pair %s=%s",
+ k, descr ? heim_string_get_utf8(descr) : "<unprintable>");
+ addkv(r, key, value);
+ heim_release(key);
+ heim_release(descr);
+}
+
+void
+heim_audit_setkv_object(heim_svc_req_desc r, const char *k, heim_object_t value)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_string_t descr;
+
+ if (key == NULL)
+ return;
+
+ descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL);
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_object(): "
+ "setting kv pair %s=%s",
+ k, descr ? heim_string_get_utf8(descr) : "<unprintable>");
+ heim_dict_set_value(r->kv, key, value);
+ heim_release(key);
+ heim_release(descr);
+}
+
+heim_object_t
+heim_audit_getkv(heim_svc_req_desc r, const char *k)
+{
+ heim_string_t key;
+ heim_object_t value;
+
+ key = heim_string_create(k);
+ if (key == NULL)
+ return NULL;
+
+ value = heim_dict_get_value(r->kv, key);
+ heim_release(key);
+ return value;
+}
+
+struct heim_audit_kv_buf {
+ char buf[1024];
+ size_t pos;
+ heim_object_t iter;
+};
+
+static void
+audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg);
+
+static void
+audit_trail_iterator_array(heim_object_t value, void *arg, int *stop)
+{
+ struct heim_audit_kv_buf *kvb = arg;
+
+ audit_trail_iterator(kvb->iter, value, kvb);
+}
+
+static void
+audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct heim_audit_kv_buf *kvb = arg;
+ char num[32];
+ const char *k = heim_string_get_utf8(key), *v = NULL;
+ char *b64 = NULL;
+
+ if (k == NULL || *k == '#') /* # keys are hidden */
+ return;
+
+ switch (heim_get_tid(value)) {
+ case HEIM_TID_STRING:
+ v = heim_string_get_utf8(value);
+ break;
+ case HEIM_TID_NUMBER:
+ snprintf(num, sizeof(num), "%lld", (long long)heim_number_get_long(value));
+ v = num;
+ break;
+ case HEIM_TID_NULL:
+ v = "null";
+ break;
+ case HEIM_TID_BOOL:
+ v = heim_bool_val(value) ? "true" : "false";
+ break;
+ case HEIM_TID_ARRAY:
+ if (kvb->iter)
+ break; /* arrays cannot be nested */
+
+ kvb->iter = key;
+ heim_array_iterate_f(value, kvb, audit_trail_iterator_array);
+ kvb->iter = NULL;
+ break;
+ case HEIM_TID_DATA: {
+ const heim_octet_string *data = heim_data_get_data(value);
+ if (rk_base64_encode(data->data, data->length, &b64) >= 0)
+ v = b64;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (v == NULL)
+ return;
+
+ if (kvb->pos < sizeof(kvb->buf) - 1)
+ kvb->buf[kvb->pos++] = ' ';
+ for (; *k && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++)
+ kvb->buf[kvb->pos] = *k++;
+ if (kvb->pos < sizeof(kvb->buf) - 1)
+ kvb->buf[kvb->pos++] = '=';
+ for (; *v && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++)
+ kvb->buf[kvb->pos] = *v++;
+
+ free(b64);
+}
+
+void
+heim_audit_trail(heim_svc_req_desc r, heim_error_code ret, const char *retname)
+{
+ const char *retval;
+ struct heim_audit_kv_buf kvb;
+ char retvalbuf[30]; /* Enough for UNKNOWN-%d */
+
+#define CASE(x) case x : retval = #x; break
+ if (retname) {
+ retval = retname;
+ } else switch (ret ? ret : r->error_code) {
+ CASE(ENOMEM);
+ CASE(ENOENT);
+ CASE(EACCES);
+ case 0:
+ retval = "SUCCESS";
+ break;
+ default:
+ /* Wish we had a com_err number->symbolic name function */
+ (void) snprintf(retvalbuf, sizeof(retvalbuf), "UNKNOWN-%d",
+ ret ? ret : r->error_code);
+ retval = retvalbuf;
+ break;
+ }
+
+ heim_audit_addkv_timediff(r, "elapsed", &r->tv_start, &r->tv_end);
+ if (r->e_text && r->kv)
+ heim_audit_addkv(r, HEIM_SVC_AUDIT_VIS, "e-text", "%s", r->e_text);
+
+ memset(&kvb, 0, sizeof(kvb));
+ if (r->kv)
+ heim_dict_iterate_f(r->kv, &kvb, audit_trail_iterator);
+ kvb.buf[kvb.pos] = '\0';
+
+ heim_log(r->hcontext, r->logf, 3, "%s %s %s %s %s%s%s%s",
+ r->reqtype, retval, r->from,
+ r->cname ? r->cname : "<unknown>",
+ r->sname ? r->sname : "<unknown>",
+ kvb.buf, r->reason ? " reason=" : "",
+ r->reason ? heim_string_get_utf8(r->reason) : "");
+}
diff --git a/lib/base/number.c b/lib/base/number.c
index c259f69971d0..8833c8b15233 100644
--- a/lib/base/number.c
+++ b/lib/base/number.c
@@ -35,7 +35,7 @@
#include "baselocl.h"
-static void
+static void HEIM_CALLCONV
number_dealloc(void *ptr)
{
}
@@ -58,12 +58,12 @@ number_cmp(void *a, void *b)
return na - nb;
}
-static unsigned long
+static uintptr_t
number_hash(void *ptr)
{
if (heim_base_is_tagged_object(ptr))
return heim_base_tagged_object_value(ptr);
- return (unsigned long)*(int *)ptr;
+ return (uintptr_t)*(int64_t *)ptr;
}
struct heim_type_data _heim_number_object = {
@@ -86,16 +86,16 @@ struct heim_type_data _heim_number_object = {
*/
heim_number_t
-heim_number_create(int number)
+heim_number_create(int64_t 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));
+ n = _heim_alloc_object(&_heim_number_object, sizeof(int64_t));
if (n)
- *((int *)n) = number;
+ *((int64_t *)n) = number;
return n;
}
@@ -124,5 +124,13 @@ 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;
+ return (int)(*(int64_t *)number);
+}
+
+int64_t
+heim_number_get_long(heim_number_t number)
+{
+ if (heim_base_is_tagged_object(number))
+ return heim_base_tagged_object_value(number);
+ return *(int64_t *)number;
}
diff --git a/lib/base/plugin.c b/lib/base/plugin.c
new file mode 100644
index 000000000000..87bb5f7ac640
--- /dev/null
+++ b/lib/base/plugin.c
@@ -0,0 +1,785 @@
+/*
+ * Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2018 AuriStor, Inc.
+ *
+ * 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 "common_plugin.h"
+
+/*
+ * Documentation for the Heimdal plugin system is in lib/krb5/plugin.c and
+ * lib/krb5/krb5-plugin.7.
+ */
+
+/*
+ * Definitions:
+ *
+ * module - a category of plugin module, identified by subsystem
+ * (e.g., "krb5")
+ * dso - a library for a module containing a map of plugin
+ * types to plugins (e.g. "service_locator")
+ * plugin - a set of callbacks and state that follows the
+ * common plugin module definition (version, init, fini)
+ *
+ * Obviously it would have been clearer to use the term "module" rather than
+ * "DSO" given there is an internal "DSO", but "module" was already taken...
+ *
+ * modules := { module: dsos }
+ * dsos := { path, dsohandle, plugins-by-name }
+ * plugins-by-name := { plugin-name: [plug] }
+ * plug := { ftable, ctx }
+ */
+
+/* global module use, use copy_modules() accessor to access */
+static heim_dict_t __modules;
+
+static HEIMDAL_MUTEX modules_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+static void
+copy_modules_once(void *context)
+{
+ heim_dict_t *modules = (heim_dict_t *)context;
+
+ *modules = heim_dict_create(11);
+ heim_assert(*modules, "plugin modules array allocation failure");
+}
+
+/* returns global modules list, refcount +1 */
+static heim_dict_t
+copy_modules(void)
+{
+ static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT;
+
+ heim_base_once_f(&modules_once, &__modules, copy_modules_once);
+
+ return heim_retain(__modules);
+}
+
+/* returns named module, refcount +1 */
+static heim_dict_t
+copy_module(const char *name)
+{
+ heim_string_t module_name = heim_string_create(name);
+ heim_dict_t modules = copy_modules();
+ heim_dict_t module;
+
+ module = heim_dict_copy_value(modules, module_name);
+ if (module == NULL) {
+ module = heim_dict_create(11);
+ heim_dict_set_value(modules, module_name, module);
+ }
+
+ heim_release(modules);
+ heim_release(module_name);
+
+ return module;
+}
+
+/* DSO helpers */
+struct heim_dso {
+ heim_string_t path;
+ heim_dict_t plugins_by_name;
+ void *dsohandle;
+};
+
+static void HEIM_CALLCONV
+dso_dealloc(void *ptr)
+{
+ struct heim_dso *p = ptr;
+
+ heim_release(p->path);
+ heim_release(p->plugins_by_name);
+#ifdef HAVE_DLOPEN
+ if (p->dsohandle)
+ dlclose(p->dsohandle);
+#endif
+}
+
+/* returns internal "DSO" for name, refcount +1 */
+static struct heim_dso *
+copy_internal_dso(const char *name)
+{
+ heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__");
+ heim_dict_t module = copy_module(name);
+ struct heim_dso *dso;
+
+ if (module == NULL)
+ return NULL;
+
+ dso = heim_dict_copy_value(module, dso_name);
+ if (dso == NULL) {
+ dso = heim_alloc(sizeof(*dso), "heim-dso", dso_dealloc);
+
+ dso->path = dso_name;
+ dso->plugins_by_name = heim_dict_create(11);
+
+ heim_dict_set_value(module, dso_name, dso);
+ }
+
+ heim_release(module);
+
+ return dso;
+}
+
+struct heim_plugin {
+ heim_plugin_common_ftable_const_p ftable;
+ void *ctx;
+};
+
+static void HEIM_CALLCONV
+plugin_free(void *ptr)
+{
+ struct heim_plugin *pl = ptr;
+
+ if (pl->ftable && pl->ftable->fini)
+ pl->ftable->fini(pl->ctx);
+}
+
+struct heim_plugin_register_ctx {
+ const void *symbol;
+ int is_dup;
+};
+
+static void
+plugin_register_check_dup(heim_object_t value, void *ctx, int *stop)
+{
+ struct heim_plugin_register_ctx *pc = ctx;
+ struct heim_plugin *pl = value;
+
+ if (pl->ftable == pc->symbol) {
+ pc->is_dup = 1;
+ *stop = 1;
+ }
+}
+
+/**
+ * Register a plugin symbol name of specific type.
+ * @param context a Keberos context
+ * @param module name of plugin module (e.g., "krb5")
+ * @param name name of plugin symbol (e.g., "krb5_plugin_kuserok")
+ * @param ftable a pointer to a function pointer table
+ * @return In case of error a non zero error com_err error is returned
+ * and the Kerberos error string is set.
+ *
+ * @ingroup heim_support
+ */
+
+heim_error_code
+heim_plugin_register(heim_context context,
+ heim_pcontext pcontext,
+ const char *module,
+ const char *name,
+ const void *ftable)
+{
+ heim_error_code ret;
+ heim_array_t plugins;
+ heim_string_t hname;
+ struct heim_dso *dso;
+ struct heim_plugin_register_ctx ctx;
+
+ ctx.symbol = ftable;
+ ctx.is_dup = 0;
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ dso = copy_internal_dso(module);
+ hname = heim_string_create(name);
+ plugins = heim_dict_copy_value(dso->plugins_by_name, hname);
+ if (plugins != NULL)
+ heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup);
+ else {
+ plugins = heim_array_create();
+ heim_dict_set_value(dso->plugins_by_name, hname, plugins);
+ }
+
+ ret = 0;
+ if (!ctx.is_dup) {
+ /* Note: refactored plugin API only supports common plugin layout */
+ struct heim_plugin *pl;
+
+ pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
+ if (pl == NULL) {
+ ret = heim_enomem(context);
+ } else {
+ pl->ftable = ftable;
+ ret = pl->ftable->init(pcontext, &pl->ctx);
+ if (ret == 0) {
+ heim_array_append_value(plugins, pl);
+ heim_debug(context, 5, "Registered %s plugin", name);
+ }
+ heim_release(pl);
+ }
+ }
+
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+
+ heim_release(dso);
+ heim_release(hname);
+ heim_release(plugins);
+
+ return ret;
+}
+
+#ifdef HAVE_DLOPEN
+
+static char *
+resolve_origin(const char *di, const char *module)
+{
+#ifdef HAVE_DLADDR
+ Dl_info dl_info;
+ const char *dname;
+ char *path, *p;
+
+ if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 &&
+ strcmp(di, "$ORIGIN") != 0)
+ return strdup(di);
+
+ di += sizeof("$ORIGIN") - 1;
+
+ if (dladdr(heim_plugin_register, &dl_info) == 0) {
+ char *s = NULL;
+
+ /* dladdr() failed */
+ if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
+ return NULL;
+ return s;
+ }
+
+ dname = dl_info.dli_fname;
+#ifdef _WIN32
+ p = strrchr(dname, '\\');
+ if (p == NULL)
+#endif
+ p = strrchr(dname, '/');
+ if (p) {
+ if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1)
+ return NULL;
+ } else {
+ if (asprintf(&path, "%s%s", dname, di) == -1)
+ return NULL;
+ }
+
+ return path;
+#else
+ char *s = NULL;
+
+ if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 &&
+ strcmp(di, "$ORIGIN") != 0)
+ return strdup(di);
+ if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
+ return NULL;
+ return s;
+#endif /* HAVE_DLADDR */
+}
+
+#endif /* HAVE_DLOPEN */
+
+/**
+ * Load plugins (new system) for the given module @module from the given
+ * directory @paths.
+ *
+ * Inputs:
+ *
+ * @context A heim_context
+ * @module Name of plugin module (typically "krb5")
+ * @paths Array of directory paths where to look
+ */
+void
+heim_load_plugins(heim_context context,
+ const char *module,
+ const char **paths)
+{
+#ifdef HAVE_DLOPEN
+ heim_string_t s = heim_string_create(module);
+ heim_dict_t mod, modules;
+ struct dirent *entry;
+ heim_error_code ret;
+ const char **di;
+ char *dirname = NULL;
+ DIR *d;
+#ifdef _WIN32
+ char *plugin_prefix;
+ size_t plugin_prefix_len;
+
+ if (asprintf(&plugin_prefix, "plugin_%s_", module) == -1)
+ return;
+ plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
+#endif
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ modules = copy_modules();
+
+ mod = heim_dict_copy_value(modules, s);
+ if (mod == NULL) {
+ mod = heim_dict_create(11);
+ if (mod == NULL) {
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+ heim_release(s);
+ heim_release(modules);
+ heim_debug(context, 5, "Load plugins for module %s failed", module);
+ return;
+ }
+ heim_dict_set_value(modules, s, mod);
+ }
+ heim_release(s);
+ heim_release(modules);
+
+ for (di = paths; *di != NULL; di++) {
+ free(dirname);
+ dirname = resolve_origin(*di, module);
+ if (dirname == NULL) {
+ heim_debug(context, 10, "Could not resolve %s", *di);
+ continue;
+ }
+ d = opendir(dirname);
+ if (d == NULL) {
+ heim_debug(context, 10, "No such directory %s", dirname);
+ continue;
+ }
+ rk_cloexec_dir(d);
+
+ heim_debug(context, 10, "Load plugins for module %s; search %s (%s)",
+ module, *di, dirname);
+
+ while ((entry = readdir(d)) != NULL) {
+ char *n = entry->d_name;
+ char *path = NULL;
+ heim_string_t spath;
+ struct heim_dso *p;
+
+ /* skip . and .. */
+ if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
+ continue;
+
+ ret = 0;
+#ifdef _WIN32
+ /*
+ * On Windows, plugins must be loaded from the same directory as
+ * heimdal.dll (typically the assembly directory) and must have
+ * the name form "plugin_<module>_<name>.dll".
+ */
+ {
+ char *ext;
+
+ if (strnicmp(n, plugin_prefix, plugin_prefix_len) != 0)
+ continue;
+ ext = strrchr(n, '.');
+ if (ext == NULL || stricmp(ext, ".dll") != 0)
+ continue;
+
+ ret = asprintf(&path, "%s\\%s", dirname, n);
+ if (ret < 0 || path == NULL)
+ continue;
+ }
+#endif
+#ifdef __APPLE__
+ { /* support loading bundles on MacOS */
+ size_t len = strlen(n);
+ if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
+ ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n);
+ }
+#endif
+ if (ret < 0 || path == NULL)
+ ret = asprintf(&path, "%s/%s", dirname, n);
+
+ if (ret < 0 || path == NULL)
+ continue;
+
+ spath = heim_string_create(n);
+ if (spath == NULL) {
+ free(path);
+ continue;
+ }
+
+ /* check if already cached */
+ p = heim_dict_copy_value(mod, spath);
+ if (p == NULL) {
+ p = heim_alloc(sizeof(*p), "heim-dso", dso_dealloc);
+ if (p)
+ p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP);
+ if (p && p->dsohandle) {
+ p->path = heim_retain(spath);
+ p->plugins_by_name = heim_dict_create(11);
+ heim_dict_set_value(mod, spath, p);
+ heim_debug(context, 10, "Load plugins for module %s; "
+ "found DSO %s", module, path);
+ }
+ }
+ heim_release(p);
+ heim_release(spath);
+ free(path);
+ }
+ closedir(d);
+ }
+ free(dirname);
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+ heim_release(mod);
+#ifdef _WIN32
+ if (plugin_prefix)
+ free(plugin_prefix);
+#endif
+#endif /* HAVE_DLOPEN */
+}
+
+/**
+ * Unload plugins of the given @module name.
+ *
+ * Params:
+ *
+ * @module Name of module whose plusins to unload.
+ */
+void
+heim_unload_plugins(heim_context context, const char *module)
+{
+ heim_string_t sname = heim_string_create(module);
+ heim_dict_t modules;
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ modules = copy_modules();
+ heim_dict_delete_key(modules, sname);
+
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+
+ heim_release(modules);
+ heim_release(sname);
+}
+
+struct iter_ctx {
+ heim_context context;
+ heim_pcontext pcontext;
+ heim_string_t n;
+ const struct heim_plugin_data *caller;
+ int flags;
+ heim_array_t result;
+ int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *);
+ void *userctx;
+ int32_t ret;
+ int32_t plugin_no_handle_retval;
+};
+
+#ifdef HAVE_DLOPEN
+/*
+ * Add plugin from a DSO that exports the plugin structure directly. This is
+ * provided for backwards compatibility with prior versions of Heimdal, but it
+ * does not allow a module to export multiple plugins, nor does it allow
+ * instance validation.
+ */
+static heim_array_t
+add_dso_plugin_struct(heim_context context,
+ heim_pcontext pcontext,
+ const char *dsopath,
+ void *dsohandle,
+ const char *name)
+{
+ heim_error_code ret;
+ heim_plugin_common_ftable_p cpm;
+ struct heim_plugin *pl;
+ heim_array_t plugins;
+
+ if (dsohandle == NULL)
+ return NULL;
+
+ /* suppress error here because we may be looking for a different plugin type */
+ cpm = (heim_plugin_common_ftable_p)dlsym(dsohandle, name);
+ if (cpm == NULL) {
+ heim_debug(context, 15, "Symbol %s not found in %s", name, dsopath);
+ return NULL;
+ }
+
+ heim_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
+
+ pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
+
+ ret = cpm->init(pcontext, &pl->ctx);
+ if (ret) {
+ heim_warn(context, ret, "plugin %s failed to initialize", dsopath);
+ heim_release(pl);
+ return NULL;
+ }
+
+ pl->ftable = cpm;
+
+ plugins = heim_array_create();
+ heim_array_append_value(plugins, pl);
+ heim_release(pl);
+
+ return plugins;
+}
+
+static int
+validate_plugin_deps(heim_context context,
+ const struct heim_plugin_data *caller,
+ const char *dsopath,
+ heim_get_instance_func_t get_instance)
+{
+ size_t i;
+
+ if (get_instance == NULL) {
+ heim_warnx(context, "plugin %s omitted instance callback",
+ dsopath);
+ return FALSE;
+ }
+
+ for (i = 0; caller->deps[i] != NULL; i++) {
+ uintptr_t heim_instance, plugin_instance;
+
+ heim_instance = caller->get_instance(caller->deps[i]);
+ plugin_instance = get_instance(caller->deps[i]);
+
+ if (heim_instance == 0 || plugin_instance == 0)
+ continue;
+
+ if (heim_instance != plugin_instance) {
+ heim_warnx(context, "plugin %s library %s linked against different "
+ "instance of Heimdal (got %zu, us %zu)",
+ dsopath, caller->deps[i],
+ plugin_instance, heim_instance);
+ return FALSE;
+ }
+ heim_debug(context, 10, "Validated plugin library dependency %s for %s",
+ caller->deps[i], dsopath);
+ }
+
+ return TRUE;
+}
+
+/*
+ * New interface from Heimdal 8 where a DSO can export a load function
+ * that can return both a Heimdal instance identifier along with an
+ * array of plugins.
+ */
+static heim_array_t
+add_dso_plugins_load_fn(heim_context context,
+ heim_pcontext pcontext,
+ const struct heim_plugin_data *caller,
+ const char *dsopath,
+ void *dsohandle)
+{
+ heim_error_code ret;
+ heim_array_t plugins;
+ heim_plugin_load_t load_fn;
+ char *sym = NULL;
+ size_t i;
+ heim_get_instance_func_t get_instance;
+ size_t n_ftables;
+ heim_plugin_common_ftable_cp *ftables;
+
+ if (asprintf(&sym, "%s_plugin_load", caller->name) == -1 || sym == NULL)
+ return NULL;
+
+ /* suppress error here because we may be looking for a different plugin type */
+ load_fn = (heim_plugin_load_t)dlsym(dsohandle, sym);
+ if (load_fn == NULL) {
+ heim_debug(context, 15, "Symbol %s not found in %s", sym, dsopath);
+ free(sym);
+ return NULL;
+ }
+
+ ret = load_fn(pcontext, &get_instance, &n_ftables, &ftables);
+ if (ret) {
+ heim_warn(context, ret, "plugin %s failed to load", dsopath);
+ free(sym);
+
+ /* fallback to loading structure directly */
+ return add_dso_plugin_struct(context, pcontext, dsopath,
+ dsohandle, caller->name);
+ }
+
+ if (!validate_plugin_deps(context, caller, dsopath, get_instance)) {
+ free(sym);
+ return NULL;
+ }
+
+ plugins = heim_array_create();
+
+ for (i = 0; i < n_ftables; i++) {
+ heim_plugin_common_ftable_cp cpm = ftables[i];
+ struct heim_plugin *pl;
+
+ pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
+
+ ret = cpm->init(pcontext, &pl->ctx);
+ if (ret) {
+ heim_warn(context, ret, "plugin %s[%zu] failed to initialize",
+ dsopath, i);
+ } else {
+ pl->ftable = cpm;
+ heim_array_append_value(plugins, pl);
+ }
+ heim_release(pl);
+ }
+
+ heim_debug(context, 15, "DSO %s loaded (%s)", dsopath, sym);
+ free(sym);
+ return plugins;
+}
+#endif /* HAVE_DLOPEN */
+
+static void
+reduce_by_version(heim_object_t value, void *ctx, int *stop)
+{
+ struct iter_ctx *s = ctx;
+ struct heim_plugin *pl = value;
+
+ if (pl->ftable && pl->ftable->minor_version >= s->caller->min_version)
+ heim_array_append_value(s->result, pl);
+}
+
+static void
+search_modules(heim_object_t key, heim_object_t value, void *ctx)
+{
+ struct iter_ctx *s = ctx;
+ struct heim_dso *p = value;
+ heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n);
+
+#ifdef HAVE_DLOPEN
+ if (plugins == NULL && p->dsohandle) {
+ const char *path = heim_string_get_utf8(p->path);
+
+ plugins = add_dso_plugins_load_fn(s->context,
+ s->pcontext,
+ s->caller,
+ path,
+ p->dsohandle);
+ if (plugins) {
+ heim_dict_set_value(p->plugins_by_name, s->n, plugins);
+ heim_debug(s->context, 5, "Loaded %zu %s %s plugin%s from %s",
+ heim_array_get_length(plugins),
+ s->caller->module, s->caller->name,
+ heim_array_get_length(plugins) > 1 ? "s" : "",
+ path);
+ }
+ }
+#endif /* HAVE_DLOPEN */
+
+ if (plugins) {
+ heim_array_iterate_f(plugins, s, reduce_by_version);
+ heim_release(plugins);
+ }
+}
+
+static void
+eval_results(heim_object_t value, void *ctx, int *stop)
+{
+ struct heim_plugin *pl = value;
+ struct iter_ctx *s = ctx;
+
+ if (s->ret != s->plugin_no_handle_retval)
+ return;
+
+ s->ret = s->func(s->pcontext, pl->ftable, pl->ctx, s->userctx);
+ if (s->ret != s->plugin_no_handle_retval
+ && !(s->flags & HEIM_PLUGIN_INVOKE_ALL))
+ *stop = 1;
+}
+
+/**
+ * Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
+ * "kuserok"). Specifically, the @func is invoked once per-plugin with
+ * four arguments: the @context, the plugin symbol value (a pointer to a
+ * struct whose first three fields are the same as common_plugin_ftable),
+ * a context value produced by the plugin's init method, and @userctx.
+ *
+ * @func should unpack arguments for a plugin function and invoke it
+ * with arguments taken from @userctx. @func should save plugin
+ * outputs, if any, in @userctx.
+ *
+ * All loaded and registered plugins are invoked via @func until @func
+ * returns something other than @nohandle. Plugins that have nothing to
+ * do for the given arguments should return the same value as @nohandle.
+ *
+ * Inputs:
+ *
+ * @context A heim_context
+ * @pcontext A context for the plugin, such as a krb5_context
+ * @module Name of module (typically "krb5")
+ * @name Name of pluggable interface (e.g., "kuserok")
+ * @min_version Lowest acceptable plugin minor version number
+ * @flags Flags (none defined at this time)
+ * @nohandle Flags (none defined at this time)
+ * @userctx Callback data for the callback function @func
+ * @func A callback function, invoked once per-plugin
+ *
+ * Outputs: None, other than the return value and such outputs as are
+ * gathered by @func.
+ */
+heim_error_code
+heim_plugin_run_f(heim_context context,
+ heim_pcontext pcontext,
+ const struct heim_plugin_data *caller,
+ int flags,
+ int32_t nohandle,
+ void *userctx,
+ int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *))
+{
+ heim_string_t m = heim_string_create(caller->module);
+ heim_dict_t modules, dict = NULL;
+ struct iter_ctx s;
+
+ s.context = context;
+ s.pcontext = pcontext;
+ s.caller = caller;
+ s.n = heim_string_create(caller->name);
+ s.flags = flags;
+ s.result = heim_array_create();
+ s.func = func;
+ s.userctx = userctx;
+ s.plugin_no_handle_retval = nohandle;
+ s.ret = nohandle;
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ /* Get loaded plugins */
+ modules = copy_modules();
+ dict = heim_dict_copy_value(modules, m);
+
+ /* Add loaded plugins to s.result array */
+ if (dict)
+ heim_dict_iterate_f(dict, &s, search_modules);
+
+ /* We don't need to hold modules_mutex during plugin invocation */
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+
+ /* Invoke loaded plugins */
+ heim_array_iterate_f(s.result, &s, eval_results);
+
+ heim_release(s.result);
+ heim_release(s.n);
+ heim_release(dict);
+ heim_release(m);
+ heim_release(modules);
+
+ return s.ret;
+}
diff --git a/lib/base/string.c b/lib/base/string.c
index 35ea2182ba3e..5e79e00b18cb 100644
--- a/lib/base/string.c
+++ b/lib/base/string.c
@@ -36,7 +36,7 @@
#include "baselocl.h"
#include <string.h>
-static void
+static void HEIM_CALLCONV
string_dealloc(void *ptr)
{
heim_string_t s = ptr;
@@ -73,11 +73,11 @@ string_cmp(void *a, void *b)
return strcmp(a, b);
}
-static unsigned long
+static uintptr_t
string_hash(void *ptr)
{
const char *s = ptr;
- unsigned long n;
+ uintptr_t n;
for (n = 0; *s; ++s)
n += *s;
@@ -153,7 +153,8 @@ heim_string_create_with_bytes(const void *data, size_t len)
s = _heim_alloc_object(&_heim_string_object, len + 1);
if (s) {
- memcpy(s, data, len);
+ if (len)
+ memcpy(s, data, len);
((char *)s)[len] = '\0';
}
return s;
@@ -182,7 +183,7 @@ heim_string_create_with_format(const char *fmt, ...)
if (ret < 0 || str == NULL)
return NULL;
- s = heim_string_ref_create(str, string_dealloc);
+ s = heim_string_ref_create(str, free);
if (s == NULL)
free(str);
return s;
@@ -238,7 +239,7 @@ heim_string_t
__heim_string_constant(const char *_str)
{
static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
- static heim_base_once_t once;
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
static heim_dict_t dict = NULL;
heim_string_t s, s2;
diff --git a/lib/base/test_base.c b/lib/base/test_base.c
index 250708df2ef4..cc875d7f8b60 100644
--- a/lib/base/test_base.c
+++ b/lib/base/test_base.c
@@ -53,6 +53,7 @@
#include <sys/stat.h>
#ifndef WIN32
#include <sys/file.h>
+#include <locale.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
@@ -63,8 +64,9 @@
#include <fcntl.h>
#include "baselocl.h"
+#include "heimbase-atomics.h"
-static void
+static void HEIM_CALLCONV
memory_free(heim_object_t obj)
{
}
@@ -238,32 +240,324 @@ test_json(void)
"{ \"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, \"\"]",
+ ("[ \"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_object_t o, o2, o3;
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");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
+ /*
+ * Test string escaping:
+ *
+ * - C-like must-escapes
+ * - ASCII control character must-escapes
+ * - surrogate pairs
+ *
+ * We test round-tripping. First we parse, then we serialize, then parse,
+ * then compare the second parse to the first for equality.
+ *
+ * We do compare serialized forms in spite of their not being canonical.
+ * That means that some changes to serialization can cause failures here.
+ */
+ o = heim_json_create("\""
+ "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
+ "\x1e" /* ASCII control character w/o C-like escape */
+ "\\u00e1" /* &aacute; */
+ "\\u07ff"
+ "\\u0801"
+ "\\u8001"
+ "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\b\f\n\r\t"
+ "\x1e"
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xA0\x81"
+ "\xe8\x80\x81"
+ "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("\""
+ "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
+ "\x1e" /* ASCII control character w/o C-like escape */
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xa0\x81"
+ "\xE8\x80\x81"
+ "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\b\f\n\r\t"
+ "\x1e"
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xA0\x81"
+ "\xe8\x80\x81"
+ "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /*
+ * Test HEIM_JSON_F_ESCAPE_NON_ASCII.
+ *
+ * Also test that we get escaped non-ASCII because we're in a not-UTF-8
+ * locale, since we setlocale(LC_ALL, "C"), so we should escape non-ASCII
+ * by default.
+ */
+ o = heim_json_create("\""
+ "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
+ "\x1e" /* ASCII control character w/o C-like escape */
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xa0\x81"
+ "\xE8\x80\x81"
+ "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\b\f\n\r\t"
+ "\x1e"
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xA0\x81"
+ "\xe8\x80\x81"
+ "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001E\\u00E1\\u07FF\\u0801\\u8001"
+ "\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ heim_release(o2);
+ o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001E\\u00E1\\u07FF\\u0801\\u8001"
+ "\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test rejection of unescaped ASCII control characters */
+ o = heim_json_create("\"\b\\f\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "strict parse accepted bad input");
+ o = heim_json_create("\"\b\x1e\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "strict parse accepted bad input");
+
+ o = heim_json_create("\"\b\\f\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("\b\f", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\"", heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test bogus backslash escape */
+ o = heim_json_create("\""
+ "\\ "
+ "\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted");
+ o = heim_json_create("\""
+ "\\ "
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(" ", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\" \"", heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test truncated surrogate encoding (bottom code unit) */
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD834\\udd"
+ "\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted");
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD834\\udd"
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\xe8\x80\x81"
+ "\\uD834\\udd", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"老\\\\uD834\\\\udd\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test truncated surrogate encodings (top code unit) */
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD83"
+ "\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted");
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD83"
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\xe8\x80\x81"
+ "\\uD83", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"老\\\\uD83\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /*
+ * Test handling of truncated UTF-8 multi-byte sequences.
+ */
+ o = heim_json_create("\""
+ "\xE8\x80"
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("\xe8\x80",
+ heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(o2 == NULL, "malformed string serialized");
+ o2 = heim_json_copy_serialize(o, HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o3 == NULL, "malformed string accepted (not strict)");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(strcmp("\xe8\x80",
+ heim_string_get_utf8(o3)) == 0, "wrong string");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test handling of unescaped / embedded newline */
+ o = heim_json_create("\"\n\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted (strict)");
+ o = heim_json_create("\"\n\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("\n", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o2 != NULL, "string not serialized");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o3 != NULL, "string not accepted");
+ heim_assert(strcmp("\n", heim_string_get_utf8(o3)) == 0, "wrong string");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test handling of embedded NULs (must decode as data, not string) */
+ o = heim_json_create("\"\\u0000\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o != NULL, "string with NULs rejected");
+ heim_assert(heim_get_tid(o) == heim_data_get_type_id(), "data-tid");
+ heim_assert(heim_data_get_length(o) == 1, "wrong data length");
+ heim_assert(((const char *)heim_data_get_ptr(o))[0] == '\0',
+ "wrong data NUL");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ heim_assert(o2 != NULL, "data not serialized");
+ heim_release(o2);
+ heim_release(o);
+
+ /*
+ * Note that the trailing ']' is not part of the JSON text (which is just a
+ * string).
+ */
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");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
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");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
+ /*
+ * heim_json_eq() can't handle dicts with dicts as keys, so we don't check
+ * for round-tripping here
+ */
o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
"{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
heim_assert(o != NULL, "dict");
@@ -281,6 +575,11 @@ test_json(void)
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
heim_release(o2);
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
@@ -289,6 +588,11 @@ test_json(void)
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
heim_release(o2);
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
@@ -297,26 +601,51 @@ test_json(void)
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
heim_release(o2);
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ 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");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
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");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
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");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
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");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
@@ -325,6 +654,11 @@ test_json(void)
fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
return 1;
}
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
/* Simple fuzz test */
for (k = strlen(j[i]) - 1; k > 0; k--) {
@@ -494,7 +828,7 @@ dict_db_open(void *plug, const char *dbtype, const char *dbname,
if (error)
*error = NULL;
- if (dbtype && *dbtype && strcmp(dbtype, "dictdb"))
+ if (dbtype && *dbtype && strcmp(dbtype, "dictdb") != 0)
return EINVAL;
if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0)
return EINVAL;
@@ -646,12 +980,13 @@ test_db_iter(heim_data_t k, heim_data_t v, void *arg)
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")))
+ if (klen == strlen("msg") && strncmp(kptr, "msg", strlen("msg")) == 0 &&
+ vlen == strlen("abc") && strncmp(vptr, "abc", strlen("abc")) == 0)
*ret &= ~(1);
else if (klen == strlen("msg2") &&
- !strncmp(kptr, "msg2", strlen("msg2")) &&
- vlen == strlen("FooBar") && !strncmp(vptr, "FooBar", strlen("FooBar")))
+ strncmp(kptr, "msg2", strlen("msg2")) == 0 &&
+ vlen == strlen("FooBar") &&
+ strncmp(vptr, "FooBar", strlen("FooBar")) == 0)
*ret &= ~(2);
else
*ret |= 4;
@@ -939,11 +1274,94 @@ test_array()
return 0;
}
+/* This function tests only that heimbase-atomics.h compiles */
+static int
+test_atomics(void)
+{
+ heim_base_atomic(void *) tptr;
+ heim_base_atomic(uint32_t) tu32;
+ heim_base_atomic(uint64_t) tu64;
+
+ heim_base_atomic_init(&tptr, NULL);
+ heim_base_atomic_init(&tu32, 0);
+ heim_base_atomic_init(&tu64, 0);
+
+ if (heim_base_atomic_load(&tptr))
+ return 1;
+ if (heim_base_atomic_load(&tu32))
+ return 1;
+ if (heim_base_atomic_load(&tu64))
+ return 1;
+
+ heim_base_atomic_store(&tptr, &tptr);
+ heim_base_atomic_store(&tu32, 1);
+ heim_base_atomic_store(&tu64, 1);
+
+ if (heim_base_atomic_load(&tptr) != &tptr)
+ return 1;
+ if (heim_base_atomic_load(&tu32) != 1)
+ return 1;
+ if (heim_base_atomic_load(&tu64) != 1)
+ return 1;
+
+ if (heim_base_atomic_inc_32(&tu32) != 2 ||
+ heim_base_atomic_load(&tu32) != 2)
+ return 1;
+ if (heim_base_atomic_inc_64(&tu64) != 2 ||
+ heim_base_atomic_load(&tu64) != 2)
+ return 1;
+
+ if (heim_base_atomic_dec_32(&tu32) != 1 ||
+ heim_base_atomic_load(&tu32) != 1)
+ return 1;
+ if (heim_base_atomic_dec_64(&tu64) != 1 ||
+ heim_base_atomic_load(&tu64) != 1)
+ return 1;
+
+ heim_base_exchange_pointer(&tptr, (void *)&tu32);
+ if (heim_base_atomic_load(&tptr) != &tu32)
+ return 1;
+ heim_base_exchange_32(&tu32, 32);
+ if (heim_base_atomic_load(&tu32) != 32)
+ return 1;
+ heim_base_exchange_64(&tu64, 64);
+ if (heim_base_atomic_load(&tu64) != 64)
+ return 1;
+
+ if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tu64) != &tu32)
+ return 1;
+ if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tptr) != &tu64)
+ return 1;
+ if (heim_base_atomic_load(&tptr) != (void *)&tu64)
+ return 1;
+
+ if (heim_base_cas_32(&tu32, 32, 4) != 32)
+ return 1;
+ if (heim_base_cas_32(&tu32, 32, 4) != 4)
+ return 1;
+ if (heim_base_atomic_load(&tu32) != 4)
+ return 1;
+
+ if (heim_base_cas_64(&tu64, 64, 4) != 64)
+ return 1;
+ if (heim_base_cas_64(&tu64, 64, 4) != 4)
+ return 1;
+ if (heim_base_atomic_load(&tu64) != 4)
+ return 1;
+
+ return 0;
+}
+
int
main(int argc, char **argv)
{
int res = 0;
+#ifndef WIN32
+ setlocale(LC_ALL, "C");
+ heim_assert(!heim_locale_is_utf8(), "setlocale(LC_ALL, \"C\") failed?");
+#endif
+
res |= test_memory();
res |= test_mutex();
res |= test_rwlock();
@@ -956,6 +1374,7 @@ main(int argc, char **argv)
res |= test_db(NULL, NULL);
res |= test_db("json", argc > 1 ? argv[1] : "test_db.json");
res |= test_array();
+ res |= test_atomics();
return res ? 1 : 0;
}
diff --git a/lib/base/version-script.map b/lib/base/version-script.map
index 656277e37da2..1ba88992985b 100644
--- a/lib/base/version-script.map
+++ b/lib/base/version-script.map
@@ -1,4 +1,3 @@
-
HEIMDAL_BASE_1.0 {
global:
_bsearch_file;
@@ -6,11 +5,17 @@ HEIMDAL_BASE_1.0 {
_bsearch_file_info;
_bsearch_file_open;
_bsearch_text;
- __heim_string_constant;
DllMain;
+ et_heim_error_table;
heim_abort;
heim_abortv;
+ heim_add_debug_dest;
+ heim_add_et_list;
+ heim_addlog_dest;
+ heim_addlog_func;
+ heim_add_warn_dest;
heim_alloc;
+ _heim_alloc_object;
heim_array_append_value;
heim_array_copy_value;
heim_array_create;
@@ -19,35 +24,94 @@ HEIMDAL_BASE_1.0 {
heim_array_get_length;
heim_array_get_type_id;
heim_array_get_value;
+ heim_array_insert_value;
heim_array_iterate_f;
heim_array_iterate_reverse_f;
- heim_array_insert_value;
heim_array_set_value;
+ heim_audit_addkv;
+ heim_audit_addkv_number;
+ heim_audit_addkv_object;
+ heim_audit_addkv_timediff;
+ heim_audit_setkv_bool;
+ heim_audit_setkv_number;
+ heim_audit_setkv_object;
+ heim_audit_addreason;
+ heim_audit_getkv;
+ heim_audit_trail;
+ heim_audit_vaddkv;
+ heim_audit_vaddreason;
heim_auto_release;
heim_auto_release_create;
heim_auto_release_drain;
heim_base_once_f;
+ heim_base_mutex;
heim_bool_create;
heim_bool_val;
+ heim_clear_error_message;
+ heim_closelog;
heim_cmp;
+ heim_config_copy;
+ heim_config_file_free;
+ heim_config_free_strings;
+ heim_config_get;
+ heim_config_get_bool;
+ heim_config_get_bool_default;
+ heim_config_get_entry;
+ heim_config_get_int;
+ heim_config_get_int_default;
+ heim_config_get_list;
+ heim_config_get_next;
+ heim_config_get_string;
+ heim_config_get_string_default;
+ heim_config_get_strings;
+ heim_config_get_time;
+ heim_config_get_time_default;
+ heim_config_parse_dir_multi;
+ heim_config_parse_file;
+ heim_config_parse_file_multi;
+ heim_config_parse_string_multi;
+ heim_config_vget;
+ heim_config_vget_bool;
+ heim_config_vget_bool_default;
+ heim_config_vget_int;
+ heim_config_vget_int_default;
+ heim_config_vget_list;
+ heim_config_vget_next;
+ heim_config_vget_string;
+ heim_config_vget_string_default;
+ heim_config_vget_strings;
+ heim_config_vget_time;
+ heim_config_vget_time_default;
+ heim_context_free;
+ heim_context_get_homedir_access;
+ heim_context_get_log_utc;
+ heim_context_get_time_fmt;
+ heim_context_init;
+ heim_context_set_homedir_access;
+ heim_context_set_log_utc;
+ heim_context_set_time_fmt;
+ _heim_create_type;
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_create;
heim_data_ref_get_type_id;
heim_db_begin;
heim_db_clone;
heim_db_commit;
heim_db_copy_value;
+ heim_db_create;
heim_db_delete_key;
heim_db_get_type_id;
+ _heim_db_get_value;
heim_db_iterate_f;
- heim_db_create;
heim_db_register;
heim_db_rollback;
heim_db_set_value;
+ heim_debug;
+ heim_description;
heim_dict_copy_value;
heim_dict_create;
heim_dict_delete_key;
@@ -55,40 +119,93 @@ HEIMDAL_BASE_1.0 {
heim_dict_get_value;
heim_dict_iterate_f;
heim_dict_set_value;
+ heim_enomem;
heim_error_append;
heim_error_copy_string;
- heim_error_create_opt;
heim_error_create;
- heim_error_createv;
heim_error_create_enomem;
+ heim_error_create_opt;
+ heim_error_createv;
heim_error_get_code;
+ heim_expand_path_tokens;
+ heim_expand_path_tokensv;
+ heim_free_config_files;
+ heim_free_error_message;
+ heim_get_debug_dest;
+ heim_get_default_config_files;
+ heim_get_error_message;
+ heim_get_error_string;
heim_get_hash;
+ _heim_get_isa;
+ _heim_get_isaextra;
+ heim_get_log_dest;
heim_get_tid;
+ heim_get_warn_dest;
+ heim_have_debug;
+ heim_have_error_string;
+ heim_initlog;
+ heim_json_copy_serialize;
heim_json_create;
heim_json_create_with_bytes;
- heim_json_copy_serialize;
+ heim_json_eq;
+ heim_load_plugins;
+ heim_locale_is_utf8;
+ heim_log;
+ heim_log_msg;
+ _heim_make_permanent;
heim_null_create;
heim_number_create;
heim_number_get_int;
+ heim_number_get_long;
heim_number_get_type_id;
+ heim_openlog;
+ heim_path_copy;
heim_path_create;
heim_path_delete;
heim_path_get;
- heim_path_copy;
+ heim_path_vcopy;
heim_path_vcreate;
heim_path_vdelete;
heim_path_vget;
- heim_path_vcopy;
+ heim_plugin_register;
+ heim_plugin_run_f;
+ heim_prepend_config_files;
+ heim_prepend_config_files_default;
+ heim_prepend_error_message;
heim_release;
heim_retain;
+ heim_set_config_files;
+ heim_set_debug_dest;
+ heim_set_error_message;
+ heim_set_log_dest;
+ heim_set_warn_dest;
heim_show;
heim_sorted_text_file_dbtype;
+ __heim_string_constant;
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;
+ _heim_type_get_tid;
+ heim_unload_plugins;
+ heim_vdebug;
+ heim_vlog;
+ heim_vlog_msg;
+ heim_vprepend_error_message;
+ heim_vset_error_message;
+ heim_vwarn;
+ heim_vwarnx;
+ heim_w32_delete_key;
+ heim_w32_getspecific;
+ heim_w32_key_create;
+ heim_w32_service_thread_detach;
+ heim_w32_setspecific;
+ heim_warn;
+ heim_warnx;
+ initialize_heim_error_table;
+ initialize_heim_error_table_r;
local:
*;
};
diff --git a/lib/base/warn.c b/lib/base/warn.c
new file mode 100644
index 000000000000..3dfed48fdac8
--- /dev/null
+++ b/lib/base/warn.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 1997 - 2020 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.
+ */
+
+#if defined(_MSC_VER)
+# pragma warning(disable: 4646)
+# pragma warning(disable: 4716)
+#endif
+
+#include "baselocl.h"
+#include <err.h>
+
+static heim_error_code _warnerr(heim_context context, int do_errtext,
+ heim_error_code code, int level, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 5, 0)));
+
+static heim_error_code
+_warnerr(heim_context context, int do_errtext,
+ heim_error_code code, int level, const char *fmt, va_list ap)
+{
+ char xfmt[7] = "";
+ const char *args[2], **arg;
+ char *msg = NULL;
+ const char *err_str = NULL;
+ heim_error_code ret;
+
+ args[0] = args[1] = NULL;
+ arg = args;
+ if(fmt){
+ strlcat(xfmt, "%s", sizeof(xfmt));
+ if(do_errtext)
+ strlcat(xfmt, ": ", sizeof(xfmt));
+ ret = vasprintf(&msg, fmt, ap);
+ if(ret < 0 || msg == NULL)
+ return ENOMEM;
+ *arg++ = msg;
+ }
+ if (do_errtext) {
+ strlcat(xfmt, "%s", sizeof(xfmt));
+
+ err_str = heim_get_error_message(context, code);
+ if (err_str != NULL) {
+ *arg = err_str;
+ } else {
+ *arg= "<unknown error>";
+ }
+ }
+
+ if (context && heim_get_warn_dest(context))
+ heim_log(context, heim_get_warn_dest(context), level, xfmt, args[0],
+ args[1]);
+ else
+ warnx(xfmt, args[0], args[1]);
+ free(msg);
+ heim_free_error_message(context, err_str);
+ return 0;
+}
+
+#define FUNC(ETEXT, CODE, LEVEL) \
+ heim_error_code ret; \
+ va_list ap; \
+ va_start(ap, fmt); \
+ ret = _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
+ va_end(ap);
+
+#undef __attribute__
+#define __attribute__(X)
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_vwarn(heim_context context, heim_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ return _warnerr(context, 1, code, 1, fmt, ap);
+}
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_warn(heim_context context, heim_error_code code, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ FUNC(1, code, 1);
+ return ret;
+}
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_vwarnx(heim_context context, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 2, 0)))
+{
+ return _warnerr(context, 0, 0, 1, fmt, ap);
+}
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_warnx(heim_context context, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)))
+{
+ FUNC(0, 0, 1);
+ return ret;
+}