summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore27
-rw-r--r--src/Makefile.am103
-rw-r--r--src/Makefile.in1584
-rw-r--r--src/deque.c181
-rw-r--r--src/dl.c52
-rw-r--r--src/format.c719
-rw-r--r--src/internal.h64
-rw-r--r--src/io.c565
-rw-r--r--src/libHX.map197
-rw-r--r--src/map.c1414
-rw-r--r--src/map_int.h128
-rw-r--r--src/mc.c237
-rw-r--r--src/misc.c88
-rw-r--r--src/opt.c986
-rw-r--r--src/proc.c269
-rw-r--r--src/rand.c123
-rw-r--r--src/rtcheck.c278
-rw-r--r--src/string.c836
-rw-r--r--src/tc-cast.c95
-rw-r--r--src/tc-compile.c39
-rw-r--r--src/tc-deque.c11
-rw-r--r--src/tc-dir.c45
-rw-r--r--src/tc-format.c90
-rw-r--r--src/tc-link.c162
-rw-r--r--src/tc-list.c151
-rw-r--r--src/tc-list2.c31
-rw-r--r--src/tc-map.c747
-rw-r--r--src/tc-memmem.c73
-rw-r--r--src/tc-misc.c46
-rw-r--r--src/tc-netio.c58
-rw-r--r--src/tc-option.c119
-rw-r--r--src/tc-proc.c70
-rw-r--r--src/tc-rand.c49
-rw-r--r--src/tc-realpath.c64
-rw-r--r--src/tc-shconfig.c63
-rw-r--r--src/tc-strchr2.c26
-rw-r--r--src/tc-string.c228
-rw-r--r--src/tc-strquote.c70
-rw-r--r--src/tc-time.c393
-rw-r--r--src/tc-xml.c35
-rw-r--r--src/time.c217
-rw-r--r--src/tx-cast.cpp1
-rw-r--r--src/tx-compile.cpp1
-rw-r--r--src/tx-deque.cpp1
-rw-r--r--src/tx-dir.cpp1
-rw-r--r--src/tx-list.cpp1
-rw-r--r--src/tx-list2.cpp1
-rw-r--r--src/tx-misc.cpp1
-rw-r--r--src/tx-netio.cpp1
-rw-r--r--src/tx-option.cpp17
-rw-r--r--src/tx-proc.cpp1
-rw-r--r--src/tx-rand.cpp1
-rw-r--r--src/tx-strchr2.cpp1
-rw-r--r--src/tx-string.cpp1
-rw-r--r--src/tx-strquote.cpp1
-rw-r--r--src/tx-time.cpp1
-rw-r--r--src/ux-file.c48
-rw-r--r--src/ux-mmap.c79
-rw-r--r--src/uxcompat.h104
59 files changed, 10995 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..5658aee
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,27 @@
+/hxdirstamp
+/t?-cast
+/t?-compile
+/t?-deque
+/t?-dir
+/t?-format
+/t?-link
+/t?-list
+/t?-list2
+/t?-map
+/t?-memmem
+/t?-misc
+/t?-netio
+/t?-option
+/t?-other
+/t?-proc
+/t?-rand
+/t?-realpath
+/t?-shconfig
+/t?-strchr2
+/t?-string
+/t?-strquote
+/t?-time
+
+# automake tests
+*.log
+*.trs
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..0ed269b
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,103 @@
+# -*- Makefile -*-
+
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include
+AM_CFLAGS = ${regular_CFLAGS}
+AM_CXXFLAGS = ${regular_CXXFLAGS}
+
+lib_LTLIBRARIES = libHX.la
+if HAVE_DLFCN_H
+lib_LTLIBRARIES += libHX_rtcheck.la
+endif
+
+libHX_la_SOURCES = deque.c dl.c format.c io.c map.c \
+ mc.c misc.c opt.c \
+ rand.c string.c time.c
+libHX_la_LIBADD = ${libdl_LIBS} ${libpthread_LIBS} ${librt_LIBS}
+libHX_la_LDFLAGS = -no-undefined -version-info 31:0:3
+if WITH_GNU_LD
+libHX_la_LDFLAGS += -Wl,--version-script=${srcdir}/libHX.map
+endif
+libHX_la_DEPENDENCIES = libHX.map
+
+if MINGW32
+libHX_la_SOURCES += ux-file.c ux-mmap.c
+endif
+if B_PROC
+libHX_la_SOURCES += proc.c
+endif
+
+libHX_rtcheck_la_SOURCES = rtcheck.c
+libHX_rtcheck_la_LIBADD = ${libdl_LIBS}
+libHX_rtcheck_la_LDFLAGS = -no-undefined -avoid-version -module
+if WITH_GNU_LD
+libHX_rtcheck_la_LDFLAGS += -Wl,--version-script=${srcdir}/libHX.map
+endif
+
+EXTRA_DIST = internal.h map_int.h libHX.map
+
+check_PROGRAMS = tc-compile tc-cast tc-deque tc-dir tc-format tc-link \
+ tc-list tc-list2 tc-map tc-memmem tc-misc tc-netio \
+ tc-option tc-proc tc-rand tc-realpath \
+ tc-shconfig tc-strchr2 tc-string tc-strquote tc-time
+TESTS = tc-strchr2 tc-strquote
+tc_cast_CFLAGS = ${AM_CFLAGS} -std=gnu99
+tc_cast_LDADD = libHX.la -lm
+tc_compile_LDADD = libHX.la
+tc_dir_LDADD = libHX.la
+tc_format_LDADD = libHX.la
+tc_link_LDADD = libHX.la
+tc_list_LDADD = libHX.la
+tc_list2_LDADD = libHX.la
+tc_list2_CFLAGS = ${AM_CFLAGS} -O2 -fstrict-aliasing
+tc_map_LDADD = libHX.la -lm
+tc_memmem_LDADD = libHX.la
+tc_misc_LDADD = libHX.la
+tc_netio_LDADD = libHX.la ${libsocket_LIBS}
+tc_option_LDADD = libHX.la
+tc_proc_LDADD = libHX.la
+tc_rand_LDADD = libHX.la
+tc_realpath_LDADD = libHX.la
+tc_shconfig_LDADD = libHX.la
+tc_strchr2_LDADD = libHX.la
+tc_string_LDADD = libHX.la
+tc_strquote_LDADD = libHX.la
+tc_time_LDADD = libHX.la
+
+if HAVE_CXX
+check_PROGRAMS += tx-compile tx-cast tx-deque tx-dir tx-list tx-list2 \
+ tx-misc tx-netio \
+ tx-option tx-proc tx-rand tx-strchr2 tx-string \
+ tx-strquote tx-time
+TESTS += tx-strchr2 tx-strquote
+tx_cast_SOURCES = tx-cast.cpp
+tx_cast_CXXFLAGS = ${AM_CXXFLAGS} -std=c++98
+tx_cast_LDADD = libHX.la -lm
+tx_compile_SOURCES = tx-compile.cpp
+tx_compile_LDADD = libHX.la
+tx_deque_SOURCES = tx-deque.cpp
+tx_dir_SOURCES = tx-dir.cpp
+tx_dir_LDADD = libHX.la
+tx_list_SOURCES = tx-list.cpp
+tx_list_LDADD = libHX.la
+tx_list2_SOURCES = tx-list2.cpp
+tx_list2_CXXFLAGS = ${AM_CXXFLAGS} -O2 -fstrict-aliasing
+tx_list2_LDADD = libHX.la
+tx_misc_SOURCES = tx-misc.cpp
+tx_misc_LDADD = libHX.la
+tx_netio_SOURCES = tx-netio.cpp
+tx_netio_LDADD = libHX.la ${libsocket_LIBS}
+tx_option_SOURCES = tx-option.cpp
+tx_option_LDADD = libHX.la
+tx_proc_SOURCES = tx-proc.cpp
+tx_proc_LDADD = libHX.la
+tx_rand_SOURCES = tx-rand.cpp
+tx_rand_LDADD = libHX.la ${librt_LIBS}
+tx_strchr2_SOURCES = tx-strchr2.cpp
+tx_strchr2_LDADD = libHX.la
+tx_string_SOURCES = tx-string.cpp
+tx_string_LDADD = libHX.la
+tx_strquote_SOURCES = tx-strquote.cpp
+tx_strquote_LDADD = libHX.la
+tx_time_SOURCES = tx-time.cpp
+tx_time_LDADD = libHX.la ${librt_LIBS}
+endif
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..7b6f318
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,1584 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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@
+
+# -*- Makefile -*-
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@HAVE_DLFCN_H_TRUE@am__append_1 = libHX_rtcheck.la
+@WITH_GNU_LD_TRUE@am__append_2 = -Wl,--version-script=${srcdir}/libHX.map
+@MINGW32_TRUE@am__append_3 = ux-file.c ux-mmap.c
+@B_PROC_TRUE@am__append_4 = proc.c
+@WITH_GNU_LD_TRUE@am__append_5 = -Wl,--version-script=${srcdir}/libHX.map
+check_PROGRAMS = tc-compile$(EXEEXT) tc-cast$(EXEEXT) \
+ tc-deque$(EXEEXT) tc-dir$(EXEEXT) tc-format$(EXEEXT) \
+ tc-link$(EXEEXT) tc-list$(EXEEXT) tc-list2$(EXEEXT) \
+ tc-map$(EXEEXT) tc-memmem$(EXEEXT) tc-misc$(EXEEXT) \
+ tc-netio$(EXEEXT) tc-option$(EXEEXT) tc-proc$(EXEEXT) \
+ tc-rand$(EXEEXT) tc-realpath$(EXEEXT) tc-shconfig$(EXEEXT) \
+ tc-strchr2$(EXEEXT) tc-string$(EXEEXT) tc-strquote$(EXEEXT) \
+ tc-time$(EXEEXT) $(am__EXEEXT_1)
+TESTS = tc-strchr2$(EXEEXT) tc-strquote$(EXEEXT) $(am__EXEEXT_2)
+@HAVE_CXX_TRUE@am__append_6 = tx-compile tx-cast tx-deque tx-dir tx-list tx-list2 \
+@HAVE_CXX_TRUE@ tx-misc tx-netio \
+@HAVE_CXX_TRUE@ tx-option tx-proc tx-rand tx-strchr2 tx-string \
+@HAVE_CXX_TRUE@ tx-strquote tx-time
+
+@HAVE_CXX_TRUE@am__append_7 = tx-strchr2 tx-strquote
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/build-aux/depcomp \
+ $(top_srcdir)/build-aux/test-driver
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gcc4_visibility.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/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)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+am__libHX_la_SOURCES_DIST = deque.c dl.c format.c io.c map.c mc.c \
+ misc.c opt.c rand.c string.c time.c ux-file.c ux-mmap.c proc.c
+@MINGW32_TRUE@am__objects_1 = ux-file.lo ux-mmap.lo
+@B_PROC_TRUE@am__objects_2 = proc.lo
+am_libHX_la_OBJECTS = deque.lo dl.lo format.lo io.lo map.lo mc.lo \
+ misc.lo opt.lo rand.lo string.lo time.lo $(am__objects_1) \
+ $(am__objects_2)
+libHX_la_OBJECTS = $(am_libHX_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 =
+libHX_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libHX_la_LDFLAGS) $(LDFLAGS) -o $@
+libHX_rtcheck_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am_libHX_rtcheck_la_OBJECTS = rtcheck.lo
+libHX_rtcheck_la_OBJECTS = $(am_libHX_rtcheck_la_OBJECTS)
+libHX_rtcheck_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libHX_rtcheck_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+@HAVE_DLFCN_H_TRUE@am_libHX_rtcheck_la_rpath = -rpath $(libdir)
+@HAVE_CXX_TRUE@am__EXEEXT_1 = tx-compile$(EXEEXT) tx-cast$(EXEEXT) \
+@HAVE_CXX_TRUE@ tx-deque$(EXEEXT) tx-dir$(EXEEXT) \
+@HAVE_CXX_TRUE@ tx-list$(EXEEXT) tx-list2$(EXEEXT) \
+@HAVE_CXX_TRUE@ tx-misc$(EXEEXT) tx-netio$(EXEEXT) \
+@HAVE_CXX_TRUE@ tx-option$(EXEEXT) tx-proc$(EXEEXT) \
+@HAVE_CXX_TRUE@ tx-rand$(EXEEXT) tx-strchr2$(EXEEXT) \
+@HAVE_CXX_TRUE@ tx-string$(EXEEXT) tx-strquote$(EXEEXT) \
+@HAVE_CXX_TRUE@ tx-time$(EXEEXT)
+tc_cast_SOURCES = tc-cast.c
+tc_cast_OBJECTS = tc_cast-tc-cast.$(OBJEXT)
+tc_cast_DEPENDENCIES = libHX.la
+tc_cast_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(tc_cast_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+tc_compile_SOURCES = tc-compile.c
+tc_compile_OBJECTS = tc-compile.$(OBJEXT)
+tc_compile_DEPENDENCIES = libHX.la
+tc_deque_SOURCES = tc-deque.c
+tc_deque_OBJECTS = tc-deque.$(OBJEXT)
+tc_deque_LDADD = $(LDADD)
+tc_dir_SOURCES = tc-dir.c
+tc_dir_OBJECTS = tc-dir.$(OBJEXT)
+tc_dir_DEPENDENCIES = libHX.la
+tc_format_SOURCES = tc-format.c
+tc_format_OBJECTS = tc-format.$(OBJEXT)
+tc_format_DEPENDENCIES = libHX.la
+tc_link_SOURCES = tc-link.c
+tc_link_OBJECTS = tc-link.$(OBJEXT)
+tc_link_DEPENDENCIES = libHX.la
+tc_list_SOURCES = tc-list.c
+tc_list_OBJECTS = tc-list.$(OBJEXT)
+tc_list_DEPENDENCIES = libHX.la
+tc_list2_SOURCES = tc-list2.c
+tc_list2_OBJECTS = tc_list2-tc-list2.$(OBJEXT)
+tc_list2_DEPENDENCIES = libHX.la
+tc_list2_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(tc_list2_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+tc_map_SOURCES = tc-map.c
+tc_map_OBJECTS = tc-map.$(OBJEXT)
+tc_map_DEPENDENCIES = libHX.la
+tc_memmem_SOURCES = tc-memmem.c
+tc_memmem_OBJECTS = tc-memmem.$(OBJEXT)
+tc_memmem_DEPENDENCIES = libHX.la
+tc_misc_SOURCES = tc-misc.c
+tc_misc_OBJECTS = tc-misc.$(OBJEXT)
+tc_misc_DEPENDENCIES = libHX.la
+tc_netio_SOURCES = tc-netio.c
+tc_netio_OBJECTS = tc-netio.$(OBJEXT)
+tc_netio_DEPENDENCIES = libHX.la $(am__DEPENDENCIES_1)
+tc_option_SOURCES = tc-option.c
+tc_option_OBJECTS = tc-option.$(OBJEXT)
+tc_option_DEPENDENCIES = libHX.la
+tc_proc_SOURCES = tc-proc.c
+tc_proc_OBJECTS = tc-proc.$(OBJEXT)
+tc_proc_DEPENDENCIES = libHX.la
+tc_rand_SOURCES = tc-rand.c
+tc_rand_OBJECTS = tc-rand.$(OBJEXT)
+tc_rand_DEPENDENCIES = libHX.la
+tc_realpath_SOURCES = tc-realpath.c
+tc_realpath_OBJECTS = tc-realpath.$(OBJEXT)
+tc_realpath_DEPENDENCIES = libHX.la
+tc_shconfig_SOURCES = tc-shconfig.c
+tc_shconfig_OBJECTS = tc-shconfig.$(OBJEXT)
+tc_shconfig_DEPENDENCIES = libHX.la
+tc_strchr2_SOURCES = tc-strchr2.c
+tc_strchr2_OBJECTS = tc-strchr2.$(OBJEXT)
+tc_strchr2_DEPENDENCIES = libHX.la
+tc_string_SOURCES = tc-string.c
+tc_string_OBJECTS = tc-string.$(OBJEXT)
+tc_string_DEPENDENCIES = libHX.la
+tc_strquote_SOURCES = tc-strquote.c
+tc_strquote_OBJECTS = tc-strquote.$(OBJEXT)
+tc_strquote_DEPENDENCIES = libHX.la
+tc_time_SOURCES = tc-time.c
+tc_time_OBJECTS = tc-time.$(OBJEXT)
+tc_time_DEPENDENCIES = libHX.la
+am__tx_cast_SOURCES_DIST = tx-cast.cpp
+@HAVE_CXX_TRUE@am_tx_cast_OBJECTS = tx_cast-tx-cast.$(OBJEXT)
+tx_cast_OBJECTS = $(am_tx_cast_OBJECTS)
+@HAVE_CXX_TRUE@tx_cast_DEPENDENCIES = libHX.la
+tx_cast_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(tx_cast_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am__tx_compile_SOURCES_DIST = tx-compile.cpp
+@HAVE_CXX_TRUE@am_tx_compile_OBJECTS = tx-compile.$(OBJEXT)
+tx_compile_OBJECTS = $(am_tx_compile_OBJECTS)
+@HAVE_CXX_TRUE@tx_compile_DEPENDENCIES = libHX.la
+am__tx_deque_SOURCES_DIST = tx-deque.cpp
+@HAVE_CXX_TRUE@am_tx_deque_OBJECTS = tx-deque.$(OBJEXT)
+tx_deque_OBJECTS = $(am_tx_deque_OBJECTS)
+tx_deque_LDADD = $(LDADD)
+am__tx_dir_SOURCES_DIST = tx-dir.cpp
+@HAVE_CXX_TRUE@am_tx_dir_OBJECTS = tx-dir.$(OBJEXT)
+tx_dir_OBJECTS = $(am_tx_dir_OBJECTS)
+@HAVE_CXX_TRUE@tx_dir_DEPENDENCIES = libHX.la
+am__tx_list_SOURCES_DIST = tx-list.cpp
+@HAVE_CXX_TRUE@am_tx_list_OBJECTS = tx-list.$(OBJEXT)
+tx_list_OBJECTS = $(am_tx_list_OBJECTS)
+@HAVE_CXX_TRUE@tx_list_DEPENDENCIES = libHX.la
+am__tx_list2_SOURCES_DIST = tx-list2.cpp
+@HAVE_CXX_TRUE@am_tx_list2_OBJECTS = tx_list2-tx-list2.$(OBJEXT)
+tx_list2_OBJECTS = $(am_tx_list2_OBJECTS)
+@HAVE_CXX_TRUE@tx_list2_DEPENDENCIES = libHX.la
+tx_list2_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(tx_list2_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am__tx_misc_SOURCES_DIST = tx-misc.cpp
+@HAVE_CXX_TRUE@am_tx_misc_OBJECTS = tx-misc.$(OBJEXT)
+tx_misc_OBJECTS = $(am_tx_misc_OBJECTS)
+@HAVE_CXX_TRUE@tx_misc_DEPENDENCIES = libHX.la
+am__tx_netio_SOURCES_DIST = tx-netio.cpp
+@HAVE_CXX_TRUE@am_tx_netio_OBJECTS = tx-netio.$(OBJEXT)
+tx_netio_OBJECTS = $(am_tx_netio_OBJECTS)
+@HAVE_CXX_TRUE@tx_netio_DEPENDENCIES = libHX.la $(am__DEPENDENCIES_1)
+am__tx_option_SOURCES_DIST = tx-option.cpp
+@HAVE_CXX_TRUE@am_tx_option_OBJECTS = tx-option.$(OBJEXT)
+tx_option_OBJECTS = $(am_tx_option_OBJECTS)
+@HAVE_CXX_TRUE@tx_option_DEPENDENCIES = libHX.la
+am__tx_proc_SOURCES_DIST = tx-proc.cpp
+@HAVE_CXX_TRUE@am_tx_proc_OBJECTS = tx-proc.$(OBJEXT)
+tx_proc_OBJECTS = $(am_tx_proc_OBJECTS)
+@HAVE_CXX_TRUE@tx_proc_DEPENDENCIES = libHX.la
+am__tx_rand_SOURCES_DIST = tx-rand.cpp
+@HAVE_CXX_TRUE@am_tx_rand_OBJECTS = tx-rand.$(OBJEXT)
+tx_rand_OBJECTS = $(am_tx_rand_OBJECTS)
+@HAVE_CXX_TRUE@tx_rand_DEPENDENCIES = libHX.la $(am__DEPENDENCIES_1)
+am__tx_strchr2_SOURCES_DIST = tx-strchr2.cpp
+@HAVE_CXX_TRUE@am_tx_strchr2_OBJECTS = tx-strchr2.$(OBJEXT)
+tx_strchr2_OBJECTS = $(am_tx_strchr2_OBJECTS)
+@HAVE_CXX_TRUE@tx_strchr2_DEPENDENCIES = libHX.la
+am__tx_string_SOURCES_DIST = tx-string.cpp
+@HAVE_CXX_TRUE@am_tx_string_OBJECTS = tx-string.$(OBJEXT)
+tx_string_OBJECTS = $(am_tx_string_OBJECTS)
+@HAVE_CXX_TRUE@tx_string_DEPENDENCIES = libHX.la
+am__tx_strquote_SOURCES_DIST = tx-strquote.cpp
+@HAVE_CXX_TRUE@am_tx_strquote_OBJECTS = tx-strquote.$(OBJEXT)
+tx_strquote_OBJECTS = $(am_tx_strquote_OBJECTS)
+@HAVE_CXX_TRUE@tx_strquote_DEPENDENCIES = libHX.la
+am__tx_time_SOURCES_DIST = tx-time.cpp
+@HAVE_CXX_TRUE@am_tx_time_OBJECTS = tx-time.$(OBJEXT)
+tx_time_OBJECTS = $(am_tx_time_OBJECTS)
+@HAVE_CXX_TRUE@tx_time_DEPENDENCIES = libHX.la $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+SOURCES = $(libHX_la_SOURCES) $(libHX_rtcheck_la_SOURCES) tc-cast.c \
+ tc-compile.c tc-deque.c tc-dir.c tc-format.c tc-link.c \
+ tc-list.c tc-list2.c tc-map.c tc-memmem.c tc-misc.c tc-netio.c \
+ tc-option.c tc-proc.c tc-rand.c tc-realpath.c tc-shconfig.c \
+ tc-strchr2.c tc-string.c tc-strquote.c tc-time.c \
+ $(tx_cast_SOURCES) $(tx_compile_SOURCES) $(tx_deque_SOURCES) \
+ $(tx_dir_SOURCES) $(tx_list_SOURCES) $(tx_list2_SOURCES) \
+ $(tx_misc_SOURCES) $(tx_netio_SOURCES) $(tx_option_SOURCES) \
+ $(tx_proc_SOURCES) $(tx_rand_SOURCES) $(tx_strchr2_SOURCES) \
+ $(tx_string_SOURCES) $(tx_strquote_SOURCES) $(tx_time_SOURCES)
+DIST_SOURCES = $(am__libHX_la_SOURCES_DIST) \
+ $(libHX_rtcheck_la_SOURCES) tc-cast.c tc-compile.c tc-deque.c \
+ tc-dir.c tc-format.c tc-link.c tc-list.c tc-list2.c tc-map.c \
+ tc-memmem.c tc-misc.c tc-netio.c tc-option.c tc-proc.c \
+ tc-rand.c tc-realpath.c tc-shconfig.c tc-strchr2.c tc-string.c \
+ tc-strquote.c tc-time.c $(am__tx_cast_SOURCES_DIST) \
+ $(am__tx_compile_SOURCES_DIST) $(am__tx_deque_SOURCES_DIST) \
+ $(am__tx_dir_SOURCES_DIST) $(am__tx_list_SOURCES_DIST) \
+ $(am__tx_list2_SOURCES_DIST) $(am__tx_misc_SOURCES_DIST) \
+ $(am__tx_netio_SOURCES_DIST) $(am__tx_option_SOURCES_DIST) \
+ $(am__tx_proc_SOURCES_DIST) $(am__tx_rand_SOURCES_DIST) \
+ $(am__tx_strchr2_SOURCES_DIST) $(am__tx_string_SOURCES_DIST) \
+ $(am__tx_strquote_SOURCES_DIST) $(am__tx_time_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+@HAVE_CXX_TRUE@am__EXEEXT_2 = tx-strchr2$(EXEEXT) tx-strquote$(EXEEXT)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/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)/build-aux/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LYX = @LYX@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+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_CXX = @ac_ct_CXX@
+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@
+docdir = @docdir@
+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@
+libdl_LIBS = @libdl_LIBS@
+libexecdir = @libexecdir@
+libpthread_LIBS = @libpthread_LIBS@
+librt_LIBS = @librt_LIBS@
+libsocket_LIBS = @libsocket_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+regular_CFLAGS = @regular_CFLAGS@
+regular_CPPFLAGS = @regular_CPPFLAGS@
+regular_CXXFLAGS = @regular_CXXFLAGS@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include
+AM_CFLAGS = ${regular_CFLAGS}
+AM_CXXFLAGS = ${regular_CXXFLAGS}
+lib_LTLIBRARIES = libHX.la $(am__append_1)
+libHX_la_SOURCES = deque.c dl.c format.c io.c map.c mc.c misc.c opt.c \
+ rand.c string.c time.c $(am__append_3) $(am__append_4)
+libHX_la_LIBADD = ${libdl_LIBS} ${libpthread_LIBS} ${librt_LIBS}
+libHX_la_LDFLAGS = -no-undefined -version-info 31:0:3 $(am__append_2)
+libHX_la_DEPENDENCIES = libHX.map
+libHX_rtcheck_la_SOURCES = rtcheck.c
+libHX_rtcheck_la_LIBADD = ${libdl_LIBS}
+libHX_rtcheck_la_LDFLAGS = -no-undefined -avoid-version -module \
+ $(am__append_5)
+EXTRA_DIST = internal.h map_int.h libHX.map
+tc_cast_CFLAGS = ${AM_CFLAGS} -std=gnu99
+tc_cast_LDADD = libHX.la -lm
+tc_compile_LDADD = libHX.la
+tc_dir_LDADD = libHX.la
+tc_format_LDADD = libHX.la
+tc_link_LDADD = libHX.la
+tc_list_LDADD = libHX.la
+tc_list2_LDADD = libHX.la
+tc_list2_CFLAGS = ${AM_CFLAGS} -O2 -fstrict-aliasing
+tc_map_LDADD = libHX.la -lm
+tc_memmem_LDADD = libHX.la
+tc_misc_LDADD = libHX.la
+tc_netio_LDADD = libHX.la ${libsocket_LIBS}
+tc_option_LDADD = libHX.la
+tc_proc_LDADD = libHX.la
+tc_rand_LDADD = libHX.la
+tc_realpath_LDADD = libHX.la
+tc_shconfig_LDADD = libHX.la
+tc_strchr2_LDADD = libHX.la
+tc_string_LDADD = libHX.la
+tc_strquote_LDADD = libHX.la
+tc_time_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_cast_SOURCES = tx-cast.cpp
+@HAVE_CXX_TRUE@tx_cast_CXXFLAGS = ${AM_CXXFLAGS} -std=c++98
+@HAVE_CXX_TRUE@tx_cast_LDADD = libHX.la -lm
+@HAVE_CXX_TRUE@tx_compile_SOURCES = tx-compile.cpp
+@HAVE_CXX_TRUE@tx_compile_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_deque_SOURCES = tx-deque.cpp
+@HAVE_CXX_TRUE@tx_dir_SOURCES = tx-dir.cpp
+@HAVE_CXX_TRUE@tx_dir_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_list_SOURCES = tx-list.cpp
+@HAVE_CXX_TRUE@tx_list_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_list2_SOURCES = tx-list2.cpp
+@HAVE_CXX_TRUE@tx_list2_CXXFLAGS = ${AM_CXXFLAGS} -O2 -fstrict-aliasing
+@HAVE_CXX_TRUE@tx_list2_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_misc_SOURCES = tx-misc.cpp
+@HAVE_CXX_TRUE@tx_misc_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_netio_SOURCES = tx-netio.cpp
+@HAVE_CXX_TRUE@tx_netio_LDADD = libHX.la ${libsocket_LIBS}
+@HAVE_CXX_TRUE@tx_option_SOURCES = tx-option.cpp
+@HAVE_CXX_TRUE@tx_option_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_proc_SOURCES = tx-proc.cpp
+@HAVE_CXX_TRUE@tx_proc_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_rand_SOURCES = tx-rand.cpp
+@HAVE_CXX_TRUE@tx_rand_LDADD = libHX.la ${librt_LIBS}
+@HAVE_CXX_TRUE@tx_strchr2_SOURCES = tx-strchr2.cpp
+@HAVE_CXX_TRUE@tx_strchr2_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_string_SOURCES = tx-string.cpp
+@HAVE_CXX_TRUE@tx_string_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_strquote_SOURCES = tx-strquote.cpp
+@HAVE_CXX_TRUE@tx_strquote_LDADD = libHX.la
+@HAVE_CXX_TRUE@tx_time_SOURCES = tx-time.cpp
+@HAVE_CXX_TRUE@tx_time_LDADD = libHX.la ${librt_LIBS}
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(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 src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libHX.la: $(libHX_la_OBJECTS) $(libHX_la_DEPENDENCIES) $(EXTRA_libHX_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libHX_la_LINK) -rpath $(libdir) $(libHX_la_OBJECTS) $(libHX_la_LIBADD) $(LIBS)
+
+libHX_rtcheck.la: $(libHX_rtcheck_la_OBJECTS) $(libHX_rtcheck_la_DEPENDENCIES) $(EXTRA_libHX_rtcheck_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libHX_rtcheck_la_LINK) $(am_libHX_rtcheck_la_rpath) $(libHX_rtcheck_la_OBJECTS) $(libHX_rtcheck_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+tc-cast$(EXEEXT): $(tc_cast_OBJECTS) $(tc_cast_DEPENDENCIES) $(EXTRA_tc_cast_DEPENDENCIES)
+ @rm -f tc-cast$(EXEEXT)
+ $(AM_V_CCLD)$(tc_cast_LINK) $(tc_cast_OBJECTS) $(tc_cast_LDADD) $(LIBS)
+
+tc-compile$(EXEEXT): $(tc_compile_OBJECTS) $(tc_compile_DEPENDENCIES) $(EXTRA_tc_compile_DEPENDENCIES)
+ @rm -f tc-compile$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_compile_OBJECTS) $(tc_compile_LDADD) $(LIBS)
+
+tc-deque$(EXEEXT): $(tc_deque_OBJECTS) $(tc_deque_DEPENDENCIES) $(EXTRA_tc_deque_DEPENDENCIES)
+ @rm -f tc-deque$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_deque_OBJECTS) $(tc_deque_LDADD) $(LIBS)
+
+tc-dir$(EXEEXT): $(tc_dir_OBJECTS) $(tc_dir_DEPENDENCIES) $(EXTRA_tc_dir_DEPENDENCIES)
+ @rm -f tc-dir$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_dir_OBJECTS) $(tc_dir_LDADD) $(LIBS)
+
+tc-format$(EXEEXT): $(tc_format_OBJECTS) $(tc_format_DEPENDENCIES) $(EXTRA_tc_format_DEPENDENCIES)
+ @rm -f tc-format$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_format_OBJECTS) $(tc_format_LDADD) $(LIBS)
+
+tc-link$(EXEEXT): $(tc_link_OBJECTS) $(tc_link_DEPENDENCIES) $(EXTRA_tc_link_DEPENDENCIES)
+ @rm -f tc-link$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_link_OBJECTS) $(tc_link_LDADD) $(LIBS)
+
+tc-list$(EXEEXT): $(tc_list_OBJECTS) $(tc_list_DEPENDENCIES) $(EXTRA_tc_list_DEPENDENCIES)
+ @rm -f tc-list$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_list_OBJECTS) $(tc_list_LDADD) $(LIBS)
+
+tc-list2$(EXEEXT): $(tc_list2_OBJECTS) $(tc_list2_DEPENDENCIES) $(EXTRA_tc_list2_DEPENDENCIES)
+ @rm -f tc-list2$(EXEEXT)
+ $(AM_V_CCLD)$(tc_list2_LINK) $(tc_list2_OBJECTS) $(tc_list2_LDADD) $(LIBS)
+
+tc-map$(EXEEXT): $(tc_map_OBJECTS) $(tc_map_DEPENDENCIES) $(EXTRA_tc_map_DEPENDENCIES)
+ @rm -f tc-map$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_map_OBJECTS) $(tc_map_LDADD) $(LIBS)
+
+tc-memmem$(EXEEXT): $(tc_memmem_OBJECTS) $(tc_memmem_DEPENDENCIES) $(EXTRA_tc_memmem_DEPENDENCIES)
+ @rm -f tc-memmem$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_memmem_OBJECTS) $(tc_memmem_LDADD) $(LIBS)
+
+tc-misc$(EXEEXT): $(tc_misc_OBJECTS) $(tc_misc_DEPENDENCIES) $(EXTRA_tc_misc_DEPENDENCIES)
+ @rm -f tc-misc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_misc_OBJECTS) $(tc_misc_LDADD) $(LIBS)
+
+tc-netio$(EXEEXT): $(tc_netio_OBJECTS) $(tc_netio_DEPENDENCIES) $(EXTRA_tc_netio_DEPENDENCIES)
+ @rm -f tc-netio$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_netio_OBJECTS) $(tc_netio_LDADD) $(LIBS)
+
+tc-option$(EXEEXT): $(tc_option_OBJECTS) $(tc_option_DEPENDENCIES) $(EXTRA_tc_option_DEPENDENCIES)
+ @rm -f tc-option$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_option_OBJECTS) $(tc_option_LDADD) $(LIBS)
+
+tc-proc$(EXEEXT): $(tc_proc_OBJECTS) $(tc_proc_DEPENDENCIES) $(EXTRA_tc_proc_DEPENDENCIES)
+ @rm -f tc-proc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_proc_OBJECTS) $(tc_proc_LDADD) $(LIBS)
+
+tc-rand$(EXEEXT): $(tc_rand_OBJECTS) $(tc_rand_DEPENDENCIES) $(EXTRA_tc_rand_DEPENDENCIES)
+ @rm -f tc-rand$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_rand_OBJECTS) $(tc_rand_LDADD) $(LIBS)
+
+tc-realpath$(EXEEXT): $(tc_realpath_OBJECTS) $(tc_realpath_DEPENDENCIES) $(EXTRA_tc_realpath_DEPENDENCIES)
+ @rm -f tc-realpath$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_realpath_OBJECTS) $(tc_realpath_LDADD) $(LIBS)
+
+tc-shconfig$(EXEEXT): $(tc_shconfig_OBJECTS) $(tc_shconfig_DEPENDENCIES) $(EXTRA_tc_shconfig_DEPENDENCIES)
+ @rm -f tc-shconfig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_shconfig_OBJECTS) $(tc_shconfig_LDADD) $(LIBS)
+
+tc-strchr2$(EXEEXT): $(tc_strchr2_OBJECTS) $(tc_strchr2_DEPENDENCIES) $(EXTRA_tc_strchr2_DEPENDENCIES)
+ @rm -f tc-strchr2$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_strchr2_OBJECTS) $(tc_strchr2_LDADD) $(LIBS)
+
+tc-string$(EXEEXT): $(tc_string_OBJECTS) $(tc_string_DEPENDENCIES) $(EXTRA_tc_string_DEPENDENCIES)
+ @rm -f tc-string$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_string_OBJECTS) $(tc_string_LDADD) $(LIBS)
+
+tc-strquote$(EXEEXT): $(tc_strquote_OBJECTS) $(tc_strquote_DEPENDENCIES) $(EXTRA_tc_strquote_DEPENDENCIES)
+ @rm -f tc-strquote$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_strquote_OBJECTS) $(tc_strquote_LDADD) $(LIBS)
+
+tc-time$(EXEEXT): $(tc_time_OBJECTS) $(tc_time_DEPENDENCIES) $(EXTRA_tc_time_DEPENDENCIES)
+ @rm -f tc-time$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tc_time_OBJECTS) $(tc_time_LDADD) $(LIBS)
+
+tx-cast$(EXEEXT): $(tx_cast_OBJECTS) $(tx_cast_DEPENDENCIES) $(EXTRA_tx_cast_DEPENDENCIES)
+ @rm -f tx-cast$(EXEEXT)
+ $(AM_V_CXXLD)$(tx_cast_LINK) $(tx_cast_OBJECTS) $(tx_cast_LDADD) $(LIBS)
+
+tx-compile$(EXEEXT): $(tx_compile_OBJECTS) $(tx_compile_DEPENDENCIES) $(EXTRA_tx_compile_DEPENDENCIES)
+ @rm -f tx-compile$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_compile_OBJECTS) $(tx_compile_LDADD) $(LIBS)
+
+tx-deque$(EXEEXT): $(tx_deque_OBJECTS) $(tx_deque_DEPENDENCIES) $(EXTRA_tx_deque_DEPENDENCIES)
+ @rm -f tx-deque$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_deque_OBJECTS) $(tx_deque_LDADD) $(LIBS)
+
+tx-dir$(EXEEXT): $(tx_dir_OBJECTS) $(tx_dir_DEPENDENCIES) $(EXTRA_tx_dir_DEPENDENCIES)
+ @rm -f tx-dir$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_dir_OBJECTS) $(tx_dir_LDADD) $(LIBS)
+
+tx-list$(EXEEXT): $(tx_list_OBJECTS) $(tx_list_DEPENDENCIES) $(EXTRA_tx_list_DEPENDENCIES)
+ @rm -f tx-list$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_list_OBJECTS) $(tx_list_LDADD) $(LIBS)
+
+tx-list2$(EXEEXT): $(tx_list2_OBJECTS) $(tx_list2_DEPENDENCIES) $(EXTRA_tx_list2_DEPENDENCIES)
+ @rm -f tx-list2$(EXEEXT)
+ $(AM_V_CXXLD)$(tx_list2_LINK) $(tx_list2_OBJECTS) $(tx_list2_LDADD) $(LIBS)
+
+tx-misc$(EXEEXT): $(tx_misc_OBJECTS) $(tx_misc_DEPENDENCIES) $(EXTRA_tx_misc_DEPENDENCIES)
+ @rm -f tx-misc$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_misc_OBJECTS) $(tx_misc_LDADD) $(LIBS)
+
+tx-netio$(EXEEXT): $(tx_netio_OBJECTS) $(tx_netio_DEPENDENCIES) $(EXTRA_tx_netio_DEPENDENCIES)
+ @rm -f tx-netio$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_netio_OBJECTS) $(tx_netio_LDADD) $(LIBS)
+
+tx-option$(EXEEXT): $(tx_option_OBJECTS) $(tx_option_DEPENDENCIES) $(EXTRA_tx_option_DEPENDENCIES)
+ @rm -f tx-option$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_option_OBJECTS) $(tx_option_LDADD) $(LIBS)
+
+tx-proc$(EXEEXT): $(tx_proc_OBJECTS) $(tx_proc_DEPENDENCIES) $(EXTRA_tx_proc_DEPENDENCIES)
+ @rm -f tx-proc$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_proc_OBJECTS) $(tx_proc_LDADD) $(LIBS)
+
+tx-rand$(EXEEXT): $(tx_rand_OBJECTS) $(tx_rand_DEPENDENCIES) $(EXTRA_tx_rand_DEPENDENCIES)
+ @rm -f tx-rand$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_rand_OBJECTS) $(tx_rand_LDADD) $(LIBS)
+
+tx-strchr2$(EXEEXT): $(tx_strchr2_OBJECTS) $(tx_strchr2_DEPENDENCIES) $(EXTRA_tx_strchr2_DEPENDENCIES)
+ @rm -f tx-strchr2$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_strchr2_OBJECTS) $(tx_strchr2_LDADD) $(LIBS)
+
+tx-string$(EXEEXT): $(tx_string_OBJECTS) $(tx_string_DEPENDENCIES) $(EXTRA_tx_string_DEPENDENCIES)
+ @rm -f tx-string$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_string_OBJECTS) $(tx_string_LDADD) $(LIBS)
+
+tx-strquote$(EXEEXT): $(tx_strquote_OBJECTS) $(tx_strquote_DEPENDENCIES) $(EXTRA_tx_strquote_DEPENDENCIES)
+ @rm -f tx-strquote$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_strquote_OBJECTS) $(tx_strquote_LDADD) $(LIBS)
+
+tx-time$(EXEEXT): $(tx_time_OBJECTS) $(tx_time_DEPENDENCIES) $(EXTRA_tx_time_DEPENDENCIES)
+ @rm -f tx-time$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(tx_time_OBJECTS) $(tx_time_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deque.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/format.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rand.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtcheck.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-compile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-deque.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-dir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-format.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-link.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-map.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-memmem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-misc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-netio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-option.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-proc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-rand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-realpath.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-shconfig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-strchr2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-string.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-strquote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-time.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc_cast-tc-cast.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc_list2-tc-list2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-compile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-deque.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-dir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-misc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-netio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-option.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-proc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-rand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-strchr2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-string.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-strquote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-time.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx_cast-tx-cast.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx_list2-tx-list2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ux-file.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ux-mmap.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 $@ $<
+
+tc_cast-tc-cast.o: tc-cast.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tc_cast_CFLAGS) $(CFLAGS) -MT tc_cast-tc-cast.o -MD -MP -MF $(DEPDIR)/tc_cast-tc-cast.Tpo -c -o tc_cast-tc-cast.o `test -f 'tc-cast.c' || echo '$(srcdir)/'`tc-cast.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tc_cast-tc-cast.Tpo $(DEPDIR)/tc_cast-tc-cast.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tc-cast.c' object='tc_cast-tc-cast.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) $(tc_cast_CFLAGS) $(CFLAGS) -c -o tc_cast-tc-cast.o `test -f 'tc-cast.c' || echo '$(srcdir)/'`tc-cast.c
+
+tc_cast-tc-cast.obj: tc-cast.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tc_cast_CFLAGS) $(CFLAGS) -MT tc_cast-tc-cast.obj -MD -MP -MF $(DEPDIR)/tc_cast-tc-cast.Tpo -c -o tc_cast-tc-cast.obj `if test -f 'tc-cast.c'; then $(CYGPATH_W) 'tc-cast.c'; else $(CYGPATH_W) '$(srcdir)/tc-cast.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tc_cast-tc-cast.Tpo $(DEPDIR)/tc_cast-tc-cast.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tc-cast.c' object='tc_cast-tc-cast.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) $(tc_cast_CFLAGS) $(CFLAGS) -c -o tc_cast-tc-cast.obj `if test -f 'tc-cast.c'; then $(CYGPATH_W) 'tc-cast.c'; else $(CYGPATH_W) '$(srcdir)/tc-cast.c'; fi`
+
+tc_list2-tc-list2.o: tc-list2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tc_list2_CFLAGS) $(CFLAGS) -MT tc_list2-tc-list2.o -MD -MP -MF $(DEPDIR)/tc_list2-tc-list2.Tpo -c -o tc_list2-tc-list2.o `test -f 'tc-list2.c' || echo '$(srcdir)/'`tc-list2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tc_list2-tc-list2.Tpo $(DEPDIR)/tc_list2-tc-list2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tc-list2.c' object='tc_list2-tc-list2.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) $(tc_list2_CFLAGS) $(CFLAGS) -c -o tc_list2-tc-list2.o `test -f 'tc-list2.c' || echo '$(srcdir)/'`tc-list2.c
+
+tc_list2-tc-list2.obj: tc-list2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tc_list2_CFLAGS) $(CFLAGS) -MT tc_list2-tc-list2.obj -MD -MP -MF $(DEPDIR)/tc_list2-tc-list2.Tpo -c -o tc_list2-tc-list2.obj `if test -f 'tc-list2.c'; then $(CYGPATH_W) 'tc-list2.c'; else $(CYGPATH_W) '$(srcdir)/tc-list2.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tc_list2-tc-list2.Tpo $(DEPDIR)/tc_list2-tc-list2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tc-list2.c' object='tc_list2-tc-list2.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) $(tc_list2_CFLAGS) $(CFLAGS) -c -o tc_list2-tc-list2.obj `if test -f 'tc-list2.c'; then $(CYGPATH_W) 'tc-list2.c'; else $(CYGPATH_W) '$(srcdir)/tc-list2.c'; fi`
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+tx_cast-tx-cast.o: tx-cast.cpp
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_cast_CXXFLAGS) $(CXXFLAGS) -MT tx_cast-tx-cast.o -MD -MP -MF $(DEPDIR)/tx_cast-tx-cast.Tpo -c -o tx_cast-tx-cast.o `test -f 'tx-cast.cpp' || echo '$(srcdir)/'`tx-cast.cpp
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tx_cast-tx-cast.Tpo $(DEPDIR)/tx_cast-tx-cast.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tx-cast.cpp' object='tx_cast-tx-cast.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_cast_CXXFLAGS) $(CXXFLAGS) -c -o tx_cast-tx-cast.o `test -f 'tx-cast.cpp' || echo '$(srcdir)/'`tx-cast.cpp
+
+tx_cast-tx-cast.obj: tx-cast.cpp
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_cast_CXXFLAGS) $(CXXFLAGS) -MT tx_cast-tx-cast.obj -MD -MP -MF $(DEPDIR)/tx_cast-tx-cast.Tpo -c -o tx_cast-tx-cast.obj `if test -f 'tx-cast.cpp'; then $(CYGPATH_W) 'tx-cast.cpp'; else $(CYGPATH_W) '$(srcdir)/tx-cast.cpp'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tx_cast-tx-cast.Tpo $(DEPDIR)/tx_cast-tx-cast.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tx-cast.cpp' object='tx_cast-tx-cast.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_cast_CXXFLAGS) $(CXXFLAGS) -c -o tx_cast-tx-cast.obj `if test -f 'tx-cast.cpp'; then $(CYGPATH_W) 'tx-cast.cpp'; else $(CYGPATH_W) '$(srcdir)/tx-cast.cpp'; fi`
+
+tx_list2-tx-list2.o: tx-list2.cpp
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_list2_CXXFLAGS) $(CXXFLAGS) -MT tx_list2-tx-list2.o -MD -MP -MF $(DEPDIR)/tx_list2-tx-list2.Tpo -c -o tx_list2-tx-list2.o `test -f 'tx-list2.cpp' || echo '$(srcdir)/'`tx-list2.cpp
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tx_list2-tx-list2.Tpo $(DEPDIR)/tx_list2-tx-list2.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tx-list2.cpp' object='tx_list2-tx-list2.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_list2_CXXFLAGS) $(CXXFLAGS) -c -o tx_list2-tx-list2.o `test -f 'tx-list2.cpp' || echo '$(srcdir)/'`tx-list2.cpp
+
+tx_list2-tx-list2.obj: tx-list2.cpp
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_list2_CXXFLAGS) $(CXXFLAGS) -MT tx_list2-tx-list2.obj -MD -MP -MF $(DEPDIR)/tx_list2-tx-list2.Tpo -c -o tx_list2-tx-list2.obj `if test -f 'tx-list2.cpp'; then $(CYGPATH_W) 'tx-list2.cpp'; else $(CYGPATH_W) '$(srcdir)/tx-list2.cpp'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tx_list2-tx-list2.Tpo $(DEPDIR)/tx_list2-tx-list2.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tx-list2.cpp' object='tx_list2-tx-list2.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tx_list2_CXXFLAGS) $(CXXFLAGS) -c -o tx_list2-tx-list2.obj `if test -f 'tx-list2.cpp'; then $(CYGPATH_W) 'tx-list2.cpp'; else $(CYGPATH_W) '$(srcdir)/tx-list2.cpp'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+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; \
+ else \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+tc-strchr2.log: tc-strchr2$(EXEEXT)
+ @p='tc-strchr2$(EXEEXT)'; \
+ b='tc-strchr2'; \
+ $(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)
+tc-strquote.log: tc-strquote$(EXEEXT)
+ @p='tc-strquote$(EXEEXT)'; \
+ b='tc-strquote'; \
+ $(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)
+tx-strchr2.log: tx-strchr2$(EXEEXT)
+ @p='tx-strchr2$(EXEEXT)'; \
+ b='tx-strchr2'; \
+ $(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)
+tx-strquote.log: tx-strquote$(EXEEXT)
+ @p='tx-strquote$(EXEEXT)'; \
+ b='tx-strquote'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; 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:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
+ clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am 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-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am 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-libLTLIBRARIES
+
+
+# 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/src/deque.c b/src/deque.c
new file mode 100644
index 0000000..3ff7dc9
--- /dev/null
+++ b/src/deque.c
@@ -0,0 +1,181 @@
+/*
+ * Double-ended queues
+ * Copyright Jan Engelhardt, 2002-2008
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libHX/deque.h>
+#include <libHX/string.h>
+#include "internal.h"
+
+static __inline__ void
+HXdeque_add(struct HXdeque_node *af, struct HXdeque_node *nd)
+{
+ struct HXdeque *parent = af->parent;
+ nd->next = af->next;
+ nd->prev = af;
+ af->next = nd;
+ nd->parent = parent;
+ if (parent->last == af)
+ parent->last = nd;
+}
+
+static __inline__ void
+HXdeque_drop(struct HXdeque *parent, struct HXdeque_node *node)
+{
+ struct HXdeque_node *left = node->prev, *right = node->next;
+
+ if (left == NULL) parent->first = right;
+ else left->next = right;
+
+ if (right == NULL) parent->last = left;
+ else right->prev = left;
+}
+
+EXPORT_SYMBOL struct HXdeque *HXdeque_init(void)
+{
+ struct HXdeque *dq;
+ if ((dq = calloc(1, sizeof(struct HXdeque))) == NULL)
+ return NULL;
+ return dq;
+}
+
+EXPORT_SYMBOL struct HXdeque_node *HXdeque_push(struct HXdeque *dq,
+ const void *ptr)
+{
+ struct HXdeque_node *nd;
+ if ((nd = malloc(sizeof(struct HXdeque_node))) == NULL)
+ return NULL;
+ nd->prev = dq->last;
+ nd->next = NULL;
+ nd->parent = dq;
+ nd->ptr = const_cast1(void *, ptr);
+
+ if (dq->first == NULL) {
+ dq->first = dq->last = nd;
+ } else {
+ dq->last->next = nd;
+ dq->last = nd;
+ }
+
+ ++dq->items;
+ return nd;
+}
+
+EXPORT_SYMBOL void *HXdeque_pop(struct HXdeque *dq)
+{
+ if (dq->last == NULL)
+ return NULL;
+ return HXdeque_del(dq->last);
+}
+
+EXPORT_SYMBOL struct HXdeque_node *HXdeque_unshift(struct HXdeque *dq,
+ const void *ptr)
+{
+ struct HXdeque_node *nd;
+
+ if (dq->first == NULL)
+ return HXdeque_push(dq, ptr);
+ if ((nd = malloc(sizeof(struct HXdeque_node))) == NULL)
+ return NULL;
+
+ nd->prev = NULL;
+ nd->next = dq->first;
+ nd->parent = dq;
+ nd->ptr = const_cast1(void *, ptr);
+
+ dq->first->prev = nd;
+ dq->first = nd;
+ ++dq->items;
+ return nd;
+}
+
+EXPORT_SYMBOL void *HXdeque_shift(struct HXdeque *dq)
+{
+ if (dq->first == NULL)
+ return NULL;
+ return HXdeque_del(dq->first);
+}
+
+EXPORT_SYMBOL void HXdeque_move(struct HXdeque_node *nd,
+ struct HXdeque_node *af)
+{
+ HXdeque_drop(nd->parent, nd);
+ HXdeque_add(af, nd);
+}
+
+EXPORT_SYMBOL void *HXdeque_del(struct HXdeque_node *node)
+{
+ void *ret = node->ptr;
+ HXdeque_drop(node->parent, node);
+ --node->parent->items;
+ free(node);
+ return ret;
+}
+
+EXPORT_SYMBOL void HXdeque_free(struct HXdeque *dq)
+{
+ struct HXdeque_node *node, *next;
+ for (node = dq->first; node != NULL; node = next) {
+ next = node->next;
+ free(node);
+ }
+ free(dq);
+}
+
+EXPORT_SYMBOL struct HXdeque_node *HXdeque_find(struct HXdeque *dq,
+ const void *ptr)
+{
+ struct HXdeque_node *travp;
+ for (travp = dq->first; travp != NULL; travp = travp->next)
+ if (travp->ptr == ptr)
+ return travp;
+ return NULL;
+}
+
+EXPORT_SYMBOL void *HXdeque_get(struct HXdeque *dq, const void *ptr)
+{
+ struct HXdeque_node *trav;
+ for (trav = dq->first; trav != NULL; trav = trav->next)
+ if (trav->ptr == ptr)
+ return trav->ptr;
+ return NULL;
+}
+
+EXPORT_SYMBOL void HXdeque_genocide2(struct HXdeque *dq, void (*xfree)(void *))
+{
+ struct HXdeque_node *trav, *next;
+ for (trav = dq->first; trav != NULL; trav = next) {
+ next = trav->next;
+ xfree(trav->ptr);
+ free(trav);
+ }
+ free(dq);
+}
+
+EXPORT_SYMBOL void **
+HXdeque_to_vec(const struct HXdeque *dq, unsigned int *num)
+{
+ const struct HXdeque_node *trav;
+ void **ret, **p;
+
+ ret = malloc((dq->items + 1) * sizeof(void *));
+ if (ret == NULL)
+ return NULL;
+
+ p = ret;
+ for (trav = dq->first; trav != NULL; trav = trav->next)
+ *p++ = trav->ptr;
+ *p = NULL;
+
+ if (num != NULL)
+ *num = dq->items;
+ return ret;
+}
diff --git a/src/dl.c b/src/dl.c
new file mode 100644
index 0000000..4c8e5fc
--- /dev/null
+++ b/src/dl.c
@@ -0,0 +1,52 @@
+/*
+ * Shared library handling
+ * Copyright Jan Engelhardt, 2007-2009
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <dlfcn.h>
+#endif
+#include <libHX/misc.h>
+#include "internal.h"
+
+EXPORT_SYMBOL void *HX_dlopen(const char *file)
+{
+#ifdef _WIN32
+ return LoadLibrary(file);
+#else
+ return dlopen(file, RTLD_LAZY);
+#endif
+}
+
+EXPORT_SYMBOL void *HX_dlsym(void *handle, const char *symbol)
+{
+#ifdef _WIN32
+ return GetProcAddress(handle, symbol);
+#else
+ return dlsym(handle, symbol);
+#endif
+}
+
+EXPORT_SYMBOL void HX_dlclose(void *handle)
+{
+#ifdef _WIN32
+ FreeLibrary(handle);
+#else
+ dlclose(handle);
+#endif
+}
+
+EXPORT_SYMBOL const char *HX_dlerror(void)
+{
+#ifdef _WIN32
+ return "[Error unavailable on WIN32]";
+#else
+ return dlerror();
+#endif
+}
diff --git a/src/format.c b/src/format.c
new file mode 100644
index 0000000..0c463b0
--- /dev/null
+++ b/src/format.c
@@ -0,0 +1,719 @@
+/*
+ * String placeholder expansion
+ * Copyright Jan Engelhardt, 2007-2010
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libHX.h>
+#include "internal.h"
+
+/* To make it easier on the highlighter */
+#define C_OPEN '('
+#define C_CLOSE ')'
+#define S_OPEN "("
+#define S_CLOSE ")"
+
+/**
+ * %HXFMT_ARGSEP_NONE: function takes only a single argument
+ * %HXFMT_ARGSEP_SPACE: split arguments at whitespace
+ * e.g. %(exec /bin/ls foo)
+ * %HXFMT_ARGSEP_COMMA: split arguments at comma
+ * e.g. %(if %(this),%(then),%(else))
+ */
+enum {
+ HXFMT_ARGSEP_NONE = 0,
+ HXFMT_ARGSEP_SPACE = 1 << 0,
+ HXFMT_ARGSEP_COMMA = 1 << 1,
+};
+
+struct fmt_entry {
+ const void *ptr;
+ unsigned int type;
+};
+
+struct func_entry {
+ hxmc_t *(*proc)(int, const hxmc_t *const *, const struct HXformat_map *);
+ char delim[4];
+};
+
+struct HXformat2_fd {
+ const char *name;
+ hxmc_t *(*proc)(int, const hxmc_t *const *, const struct HXformat_map *);
+ unsigned int flags;
+};
+
+struct HXformat_map {
+ struct HXmap *vars;
+ struct HXmap *funcs;
+};
+
+static void fmt_entry_free(void *e)
+{
+ struct fmt_entry *entry = e;
+
+ switch (entry->type) {
+ case HXTYPE_STRING | HXFORMAT_IMMED:
+ free(const_cast1(void *, entry->ptr));
+ break;
+ case HXTYPE_MCSTR | HXFORMAT_IMMED:
+ HXmc_free(const_cast1(void *, entry->ptr));
+ break;
+ }
+ free(entry);
+}
+
+static const struct HXmap_ops fmt_entry_ops = {
+ .d_free = fmt_entry_free,
+};
+
+static void *func_entry_clone(const void *data, size_t size)
+{
+ const struct HXformat2_fd *in = data;
+ struct func_entry *out;
+ unsigned int i = 0;
+
+ out = malloc(sizeof(*out));
+ if (out == NULL)
+ return NULL;
+ out->proc = in->proc;
+ memset(out->delim, '\0', sizeof(out->delim));
+ out->delim[i++] = C_CLOSE;
+ if (in->flags & HXFMT_ARGSEP_COMMA)
+ out->delim[i++] = ',';
+ if (in->flags & HXFMT_ARGSEP_SPACE)
+ out->delim[i++] = ' ';
+ return out;
+}
+
+static const struct HXmap_ops func_entry_ops = {
+ .d_clone = func_entry_clone,
+};
+
+EXPORT_SYMBOL void HXformat_free(struct HXformat_map *blk)
+{
+ HXmap_free(blk->vars);
+ HXmap_free(blk->funcs);
+ free(blk);
+}
+
+EXPORT_SYMBOL int HXformat_add(struct HXformat_map *blk, const char *key,
+ const void *ptr, unsigned int ptr_type)
+{
+ struct fmt_entry *entry;
+ int ret;
+
+ if (strpbrk(key, "\t\n\v ") != NULL || *key == '\0') {
+ fprintf(stderr, "%s: Bogus key \"%s\"\n", __func__, key);
+ return -EINVAL;
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL)
+ return -errno;
+
+ entry->type = ptr_type;
+ if (ptr_type == (HXTYPE_STRING | HXFORMAT_IMMED)) {
+ if ((entry->ptr = HX_strdup(ptr)) == NULL) {
+ free(entry);
+ return -errno;
+ }
+ } else if (ptr_type == (HXTYPE_MCSTR | HXFORMAT_IMMED)) {
+ if ((entry->ptr = HXmc_meminit(ptr, HXmc_length(ptr))) == NULL) {
+ free(entry);
+ return -errno;
+ }
+ } else {
+ entry->ptr = ptr;
+ }
+
+ ret = HXmap_add(blk->vars, key, entry);
+ if (ret <= 0) {
+ free(entry);
+ return ret;
+ }
+
+ return 1;
+}
+
+static __inline__ char *HX_strchr0(const char *s, char c)
+{
+ char *ret = strchr(s, c);
+ if (ret != NULL)
+ return ret;
+ return const_cast1(char *, s) + strlen(s);
+}
+
+/*
+ * Used as an unique object for "expanded to nothing", to distinguish it from
+ * %NULL indicating some error.
+ */
+static char HXformat2_nexp;
+
+static __inline__ void HXformat2_insuf(const char *func, int argc)
+{
+ fprintf(stderr, "%s: insufficient number of arguments (%d)\n",
+ func, argc);
+}
+
+/*
+ * Echo input back, with markers. This is strictly for testing only.
+ */
+static hxmc_t *HXformat2_echo(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ hxmc_t *ret = HXmc_meminit(NULL, 0);
+ int i;
+
+ HXmc_strcat(&ret, "<echo");
+ for (i = 0; i < argc; ++i) {
+ HXmc_strcat(&ret, " [");
+ HXmc_strcat(&ret, argv[i]);
+ HXmc_strcat(&ret, "]");
+ }
+ HXmc_strcat(&ret, ">");
+ return ret;
+}
+
+static hxmc_t *HXformat2_env(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ const char *s;
+
+ if (argc == 0)
+ return &HXformat2_nexp;
+ s = getenv(argv[0]);
+ return (s == NULL) ? &HXformat2_nexp : HXmc_strinit(s);
+}
+
+static hxmc_t *HXformat2_if(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ if (argc < 2) {
+ HXformat2_insuf(__func__, argc);
+ return &HXformat2_nexp;
+ }
+
+ if (*argv[0] != '\0')
+ return (*argv[1] != '\0') ?
+ HXmc_strinit(argv[1]) : &HXformat2_nexp;
+
+ return (argc >= 3 && *argv[2] != '\0') ?
+ HXmc_strinit(argv[2]) : &HXformat2_nexp;
+}
+
+static hxmc_t *HXformat2_lower(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ hxmc_t *ret;
+
+ if (argc == 0)
+ return &HXformat2_nexp;
+ ret = HXmc_strinit(argv[0]);
+ HX_strlower(ret);
+ return ret;
+}
+
+static hxmc_t *HXformat2_exec1(const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ struct HXproc proc = {
+ .p_flags = HXPROC_NULL_STDIN | HXPROC_STDOUT | HXPROC_VERBOSE,
+ };
+ hxmc_t *slurp, *complete = NULL;
+ ssize_t ret;
+
+ if (HXmap_find(blk->vars, "/libhx/exec") == NULL)
+ return &HXformat2_nexp;
+
+ slurp = HXmc_meminit(NULL, BUFSIZ);
+ if (slurp == NULL)
+ return NULL;
+ complete = HXmc_meminit(NULL, BUFSIZ);
+ if (complete == NULL)
+ goto out;
+
+ ret = HXproc_run_async(argv, &proc);
+ if (ret < 0)
+ goto out;
+ while ((ret = read(proc.p_stdout, slurp, BUFSIZ)) > 0)
+ if (HXmc_memcat(&complete, slurp, ret) == NULL)
+ break;
+ close(proc.p_stdout);
+ HXproc_wait(&proc);
+ HXmc_free(slurp);
+ return complete;
+ out:
+ HXmc_free(complete);
+ HXmc_free(slurp);
+ return &HXformat2_nexp;
+}
+
+static hxmc_t *HXformat2_exec(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ if (argc == 0)
+ return &HXformat2_nexp;
+ return HXformat2_exec1(argv, blk);
+}
+
+static hxmc_t *HXformat2_shell(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ const char *cmd[] = {"/bin/sh", "-c", NULL, NULL};
+ if (argc == 0)
+ return &HXformat2_nexp;
+ cmd[2] = argv[0];
+ return HXformat2_exec1(cmd, blk);
+}
+
+static hxmc_t *HXformat2_snl(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ hxmc_t *s;
+ char *p;
+
+ if (argc == 0)
+ return &HXformat2_nexp;
+ p = s = HXmc_strinit(*argv);
+ if (s == NULL)
+ return NULL;
+ HX_chomp(s);
+ while ((p = strchr(p, '\n')) != NULL)
+ *p++ = ' ';
+ return s;
+}
+
+static hxmc_t *HXformat2_substr(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ ssize_t offset, length, z;
+ hxmc_t *ret;
+ char *end;
+
+ if (argc < 2) {
+ HXformat2_insuf(__func__, argc);
+ return &HXformat2_nexp;
+ }
+
+ offset = strtoll(argv[1], &end, 0);
+ if (*end != '\0') {
+ fprintf(stderr, "HXformat2-substr: found garbage in "
+ "offset specification\n");
+ return &HXformat2_nexp;
+ }
+
+ z = strlen(argv[0]);
+ if (offset < 0)
+ offset = z + offset;
+ if (offset >= z)
+ return &HXformat2_nexp;
+
+ if (argc == 2) {
+ if (offset < 0)
+ offset = 0;
+ length = z - offset;
+ } else {
+ length = strtoll(argv[2], &end, 0);
+ if (*end != '\0') {
+ fprintf(stderr, "HXformat2-substr; found garbage in "
+ "length specification\n");
+ return &HXformat2_nexp;
+ }
+ if (length < 0)
+ length/*end*/ = z + length;
+ else
+ length/*end*/ = offset + length;
+ if (offset < 0)
+ offset = 0;
+ }
+ if (length <= 0)
+ return &HXformat2_nexp;
+
+ ret = HXmc_meminit(NULL, length);
+ if (ret == NULL)
+ return &HXformat2_nexp;
+ if (HXmc_memcpy(&ret, &argv[0][offset], length) == NULL) {
+ HXmc_free(ret);
+ return &HXformat2_nexp;
+ }
+ return ret;
+}
+
+static hxmc_t *HXformat2_upper(int argc, const hxmc_t *const *argv,
+ const struct HXformat_map *blk)
+{
+ hxmc_t *ret;
+
+ if (argc == 0)
+ return &HXformat2_nexp;
+ ret = HXmc_strinit(argv[0]);
+ HX_strupper(ret);
+ return ret;
+}
+
+static const struct HXformat2_fd HXformat2_fmap[] = {
+ {"echo", HXformat2_echo, HXFMT_ARGSEP_COMMA | HXFMT_ARGSEP_SPACE},
+ {"env", HXformat2_env, HXFMT_ARGSEP_COMMA | HXFMT_ARGSEP_SPACE},
+ {"exec", HXformat2_exec, HXFMT_ARGSEP_SPACE},
+ {"if", HXformat2_if, HXFMT_ARGSEP_COMMA},
+ {"lower", HXformat2_lower, HXFMT_ARGSEP_NONE},
+ {"shell", HXformat2_shell, HXFMT_ARGSEP_NONE},
+ {"snl", HXformat2_snl, HXFMT_ARGSEP_NONE},
+ {"substr", HXformat2_substr, HXFMT_ARGSEP_COMMA},
+ {"upper", HXformat2_upper, HXFMT_ARGSEP_NONE},
+};
+
+/**
+ * HXformat2_xcall - expand function call (gather args)
+ * @name: name of function
+ * @pptr: pointer to position in string
+ * @table: table of known variables
+ *
+ * @*pptr must point to the first character of the first argument to the
+ * function.
+ */
+static hxmc_t *HXformat2_xcall(const char *name, const char **pptr,
+ const struct HXformat_map *blk)
+{
+ const struct func_entry *entry;
+ hxmc_t *ret, *ret2, **argv;
+ struct HXdeque *dq;
+ const char *s, *delim;
+ int err = 0;
+
+ dq = HXdeque_init();
+ if (dq == NULL)
+ return NULL;
+
+ entry = HXmap_get(blk->funcs, name);
+ delim = (entry != NULL) ? entry->delim : S_CLOSE;
+ if (**pptr == C_CLOSE)
+ ++*pptr;
+ else for (s = *pptr; *s != '\0'; s = ++*pptr) {
+ while (HX_isspace(*s))
+ ++s;
+ *pptr = s;
+ ret = HXparse_dequote_fmt(s, delim, pptr);
+ if (ret == NULL)
+ goto out_h_errno;
+ if (strstr(ret, "%" S_OPEN) != NULL) {
+ ret2 = NULL;
+ err = HXformat_aprintf(blk, &ret2, ret);
+ if (err < 0 || ret2 == NULL)
+ goto out_h_neg;
+ HXmc_free(ret);
+ ret = ret2;
+ }
+ if (HXdeque_push(dq, ret) == NULL)
+ goto out_h_errno;
+ if (**pptr == '\0')
+ break;
+ if (**pptr == C_CLOSE) {
+ ++*pptr;
+ break;
+ }
+ }
+
+ ret = NULL;
+ argv = reinterpret_cast(hxmc_t **, HXdeque_to_vec(dq, NULL));
+ if (argv == NULL)
+ goto out_h_errno;
+
+ ret = &HXformat2_nexp;
+ /* Unknown functions are silently expanded to nothing, like in make. */
+ if (entry != NULL)
+ ret = entry->proc(dq->items,
+ const_cast2(const hxmc_t *const *, argv),
+ blk);
+ /*
+ * Pointers in argv are shared with those in dq.
+ * Free only the outer shell of one.
+ */
+ free(argv);
+ out:
+ HXdeque_genocide2(dq, static_cast(void *, HXmc_free));
+ errno = -err;
+ return ret;
+
+ out_h_errno:
+ err = -errno;
+ out_h_neg:
+ HXmc_free(ret);
+ ret = NULL;
+ goto out;
+}
+
+/**
+ * HXformat2_xvar - expand a variable
+ * @entry: the variable to expand
+ */
+static hxmc_t *HXformat2_xvar(const struct fmt_entry *entry)
+{
+#define IMM(fmt, type) \
+ snprintf(buf, sizeof(buf), (fmt), \
+ static_cast(type, reinterpret_cast(uintptr_t, entry->ptr))); \
+ break;
+#define PTR(fmt, type) \
+ snprintf(buf, sizeof(buf), (fmt), \
+ *static_cast(const type *, entry->ptr)); \
+ break;
+
+ static const char *const tf[] = {"false", "true"};
+ char buf[HXSIZEOF_Z64];
+ hxmc_t *wp = NULL;
+
+ *buf = '\0';
+ switch (entry->type) {
+ case HXTYPE_STRING:
+ case HXTYPE_STRING | HXFORMAT_IMMED:
+ HXmc_strcpy(&wp, entry->ptr);
+ break;
+ case HXTYPE_STRP:
+ HXmc_strcpy(&wp, *static_cast(const char *const *, entry->ptr));
+ break;
+ case HXTYPE_MCSTR:
+ case HXTYPE_MCSTR | HXFORMAT_IMMED: {
+ const hxmc_t *input = entry->ptr;
+ HXmc_memcpy(&wp, input, HXmc_length(input));
+ break;
+ }
+ case HXTYPE_BOOL:
+ HXmc_strcpy(&wp, tf[!!*static_cast(const int *,
+ entry->ptr)]);
+ break;
+ case HXTYPE_BOOL | HXFORMAT_IMMED:
+ HXmc_strcpy(&wp, tf[entry->ptr != NULL]);
+ break;
+
+ case HXTYPE_BYTE: PTR("%c", unsigned char);
+ case HXTYPE_SHORT: PTR("%hd", short);
+ case HXTYPE_USHORT: PTR("%hu", unsigned short);
+ case HXTYPE_CHAR: PTR("%d", char);
+ case HXTYPE_UCHAR: PTR("%u", unsigned char);
+ case HXTYPE_INT: PTR("%d", int);
+ case HXTYPE_UINT: PTR("%u", unsigned int);
+ case HXTYPE_LONG: PTR("%ld", long);
+ case HXTYPE_ULONG: PTR("%lu", unsigned long);
+ case HXTYPE_LLONG: PTR("%" HX_LONGLONG_FMT "d", long long);
+ case HXTYPE_ULLONG: PTR("%" HX_LONGLONG_FMT "u", unsigned long long);
+ case HXTYPE_FLOAT: PTR("%f", float);
+ case HXTYPE_DOUBLE: PTR("%f", double);
+
+ case HXTYPE_CHAR | HXFORMAT_IMMED: IMM("%d", char);
+ case HXTYPE_UCHAR | HXFORMAT_IMMED: IMM("%u", unsigned char);
+ case HXTYPE_SHORT | HXFORMAT_IMMED: IMM("%hd", short);
+ case HXTYPE_USHORT | HXFORMAT_IMMED: IMM("%hu", unsigned short);
+ case HXTYPE_INT | HXFORMAT_IMMED: IMM("%d", int);
+ case HXTYPE_UINT | HXFORMAT_IMMED: IMM("%u", unsigned int);
+ case HXTYPE_LONG | HXFORMAT_IMMED: IMM("%ld", long);
+ case HXTYPE_ULONG | HXFORMAT_IMMED: IMM("%lu", unsigned long);
+
+ default:
+ fprintf(stderr, "%s: Illegal type\n", __func__);
+ return &HXformat2_nexp;
+ }
+
+ if (*buf != '\0')
+ HXmc_strcpy(&wp, buf);
+ return wp;
+#undef IMM
+#undef PTR
+}
+
+/**
+ * HXformat2_xany - expand function call or variable
+ * @pptr: pointer to position in string
+ * @table: map of available variables
+ *
+ * @*pptr has to point to the first character after the "%(" opener.
+ */
+static hxmc_t *
+HXformat2_xany(const char **pptr, const struct HXformat_map *blk)
+{
+ const char *s = *pptr;
+ hxmc_t *name, *ret;
+
+ /*
+ * Some shortcuts for cases that always expand to nothing.
+ * %() and %( ).
+ */
+ if (*s == C_CLOSE) {
+ ++*pptr;
+ return &HXformat2_nexp;
+ } else if (HX_isspace(*s)) {
+ while (HX_isspace(*s))
+ ++s;
+ HXmc_free(HXparse_dequote_fmt(s, S_CLOSE, pptr));
+ ++*pptr;
+ return &HXformat2_nexp;
+ }
+
+ /* Long parsing */
+ name = HXparse_dequote_fmt(s, S_CLOSE " \t\n\f\v\r", pptr);
+ if (name == NULL)
+ return NULL;
+ s = *pptr;
+ if (*s == '\0') {
+ fprintf(stderr, "libHX-format2: "
+ "unterminated variable reference / "
+ "missing closing parenthesis.\n");
+ return NULL;
+ } else if (*s == C_CLOSE) {
+ /* Closing parenthesis - variable */
+ const struct fmt_entry *entry;
+ hxmc_t *new_name = NULL;
+ int eret;
+
+ *pptr = ++s;
+ eret = HXformat_aprintf(blk, &new_name, name);
+ if (eret <= 0) {
+ ret = NULL;
+ } else if (*new_name == '\0') {
+ ret = &HXformat2_nexp;
+ } else {
+ entry = HXmap_get(blk->vars, new_name);
+ ret = (entry == NULL) ? &HXformat2_nexp :
+ HXformat2_xvar(entry);
+ }
+ HXmc_free(new_name);
+ } else {
+ /* Seen whitespace - function */
+ while (HX_isspace(*s))
+ ++s;
+ *pptr = s;
+ /*
+ * Note that %() is not expanded in function names. This
+ * follows make(1) behavior.
+ */
+ ret = HXformat2_xcall(name, pptr, blk);
+ }
+
+ HXmc_free(name);
+ return ret;
+}
+
+EXPORT_SYMBOL struct HXformat_map *HXformat_init(void)
+{
+ struct HXformat_map *blk;
+ unsigned int i;
+ int saved_errno, ret;
+
+ blk = calloc(1, sizeof(*blk));
+ if (blk == NULL)
+ return NULL;
+
+ blk->vars = HXmap_init5(HXMAPT_DEFAULT, HXMAP_SCKEY, &fmt_entry_ops,
+ 0, sizeof(struct fmt_entry));
+ if (blk->vars == NULL)
+ goto out;
+ blk->funcs = HXmap_init5(HXMAPT_DEFAULT, HXMAP_SCKEY, &func_entry_ops,
+ 0, sizeof(struct func_entry));
+ if (blk->funcs == NULL)
+ goto out;
+ for (i = 0; i < ARRAY_SIZE(HXformat2_fmap); ++i) {
+ ret = HXmap_add(blk->funcs, HXformat2_fmap[i].name,
+ &HXformat2_fmap[i]);
+ if (ret < 0)
+ goto out;
+ }
+ return blk;
+
+ out:
+ saved_errno = errno;
+ if (blk->vars != NULL)
+ HXmap_free(blk->vars);
+ if (blk->funcs != NULL)
+ HXmap_free(blk->funcs);
+ free(blk);
+ errno = saved_errno;
+ return NULL;
+}
+
+EXPORT_SYMBOL int HXformat_aprintf(const struct HXformat_map *blk,
+ hxmc_t **resultp, const char *fmt)
+{
+ hxmc_t *ex, *ts, *out;
+ const char *current;
+ int ret = 0;
+
+ out = HXmc_strinit("");
+ if (out == NULL)
+ goto out;
+
+ current = fmt;
+ while ((current = HX_strchr0(fmt, '%')) != NULL) {
+ if (current - fmt > 0)
+ if (HXmc_memcat(&out, fmt, current - fmt) == NULL)
+ goto out;
+ if (*current == '\0')
+ break;
+ if (current[1] != C_OPEN) {
+ if (HXmc_memcat(&out, current, 2) == NULL)
+ goto out;
+ fmt = current + 2;
+ continue;
+ }
+
+ current += 2; /* skip % and opening parenthesis */
+ ex = HXformat2_xany(&current, blk);
+ if (ex == NULL)
+ goto out;
+ if (ex != &HXformat2_nexp) {
+ ts = HXmc_memcat(&out, ex, HXmc_length(ex));
+ HXmc_free(ex);
+ if (ts == NULL)
+ goto out;
+ }
+ fmt = current;
+ }
+
+ *resultp = out;
+ return HXmc_length(out);
+
+ out:
+ ret = -errno;
+ HXmc_free(out);
+ return ret;
+}
+
+EXPORT_SYMBOL int HXformat_fprintf(const struct HXformat_map *ftable,
+ FILE *filp, const char *fmt)
+{
+ hxmc_t *str;
+ int ret;
+
+ if ((ret = HXformat_aprintf(ftable, &str, fmt)) <= 0)
+ return ret;
+ errno = 0;
+ if (fputs(str, filp) < 0)
+ ret = -errno;
+ HXmc_free(str);
+ return ret;
+}
+
+EXPORT_SYMBOL int HXformat_sprintf(const struct HXformat_map *ftable,
+ char *dest, size_t size, const char *fmt)
+{
+ hxmc_t *str;
+ int ret;
+
+ if ((ret = HXformat_aprintf(ftable, &str, fmt)) < 0)
+ return ret;
+ if (ret == 0) {
+ *dest = '\0';
+ return 0;
+ }
+ strncpy(dest, str, size);
+ ret = HXmc_length(dest);
+ HXmc_free(str);
+ return ret;
+}
diff --git a/src/internal.h b/src/internal.h
new file mode 100644
index 0000000..969c56f
--- /dev/null
+++ b/src/internal.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright Jan Engelhardt, 1999-2008
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#ifndef LIBHX_INTERNAL_H
+#define LIBHX_INTERNAL_H 1
+
+#include "config.h"
+#include <libHX/defs.h>
+#include <libHX/string.h>
+
+#ifdef __cplusplus
+ /* Only for our dual C/C++ testsuites */
+# define const_cast(type, expr) const_cast<type>(expr)
+# define const_cast1(type, expr) const_cast<type>(expr)
+# define const_cast2(type, expr) const_cast<type>(expr)
+# define const_cast3(type, expr) const_cast<type>(expr)
+# define dynamic_cast(type, expr) dynamic_cast<type>(expr)
+# define signed_cast(type, expr) signed_cast<type>(expr)
+# define static_cast(type, expr) static_cast<type>(expr)
+# define reinterpret_cast(type, expr) reinterpret_cast<type>(expr)
+#endif
+
+#ifdef __MINGW32__
+# include "uxcompat.h"
+#endif
+#ifdef _MSC_VER
+# include "uxcompat.h"
+# define snprintf _snprintf
+#endif
+
+#ifdef HAVE_VISIBILITY_HIDDEN
+# define EXPORT_SYMBOL __attribute__((visibility("default")))
+#else
+# define EXPORT_SYMBOL
+#endif
+
+#define MAXFNLEN 256 /* max length for filename buffer */
+#define MAXLNLEN 1024 /* max length for usual line */
+
+#define HXMC_IDENT 0x200571AF
+
+struct memcont {
+ size_t alloc, length;
+ unsigned int id;
+ char data[];
+};
+
+struct timespec;
+struct timeval;
+
+extern hxmc_t *HXparse_dequote_fmt(const char *, const char *, const char **);
+
+/* time.c - these are obsolete, but kept for ABI */
+extern void HX_diff_timespec(struct timespec *, const struct timespec *,
+ const struct timespec *);
+extern void HX_diff_timeval(struct timeval *, const struct timeval *,
+ const struct timeval *);
+
+#endif /* LIBHX_INTERNAL_H */
diff --git a/src/io.c b/src/io.c
new file mode 100644
index 0000000..6d664d1
--- /dev/null
+++ b/src/io.c
@@ -0,0 +1,565 @@
+/*
+ * File and directory handling
+ * Copyright Jan Engelhardt, 2002-2011
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined _WIN32
+# include <direct.h>
+# include <io.h>
+# include <windows.h>
+#else
+# include <dirent.h>
+# include <unistd.h>
+#endif
+#include <libHX/ctype_helper.h>
+#include <libHX/defs.h>
+#include <libHX/io.h>
+#include <libHX/misc.h>
+#include <libHX/string.h>
+#include "internal.h"
+
+struct HXdir {
+#if defined _WIN32
+ char *dname;
+ HANDLE ptr;
+ WIN32_FIND_DATA dentry;
+ bool got_first;
+#else
+ DIR *ptr;
+ struct dirent dentry; /* must be last */
+#endif
+};
+
+static int mkdir_gen(const char *d, unsigned int mode)
+{
+ struct stat sb;
+ if (lstat(d, &sb) < 0) {
+#if defined(_WIN32)
+ if (mkdir(d) < 0)
+#else
+ if (mkdir(d, mode) < 0) /* use umask() for permissions */
+#endif
+ return -errno;
+ } else {
+#if defined(_WIN32)
+ if ((sb.st_mode & S_IFDIR) != S_IFDIR)
+#else
+ if (!S_ISDIR(sb.st_mode))
+#endif
+ return -errno;
+ }
+ return 1;
+}
+
+EXPORT_SYMBOL struct HXdir *HXdir_open(const char *s)
+{
+ struct HXdir *d;
+
+#ifdef _WIN32
+ if ((d = malloc(sizeof(*d))) == NULL)
+ return NULL;
+ if ((d->dname = malloc(strlen(s) + 3)) == NULL) {
+ free(d);
+ return NULL;
+ }
+ strcpy(d->dname, s);
+ strcat(d->dname, "\\*");
+ d->got_first = false;
+#else
+ /*
+ * On Linux-glibc, the struct dirent definition contains a wasteful
+ * and bug-concealing "char d_name[256]", while on Solaris, it is a
+ * proper "char d_name[]".
+ */
+ size_t size = sizeof(*d);
+ ssize_t name_max;
+ DIR *tmp_dh = opendir(s);
+ if (tmp_dh == NULL)
+ return NULL;
+ /*
+ * dirfd is POSIX.1-2008 and was present earlier in selected
+ * extensions. In case of !HAVE_DIRFD, use pathconf(s, _PC_NAME_MAX)
+ * and bite the race bullet.
+ */
+ name_max = fpathconf(dirfd(tmp_dh), _PC_NAME_MAX);
+ if (name_max > 0) {
+ size -= sizeof(d->dentry) - offsetof(struct dirent, d_name);
+ size += name_max + 1;
+ } else {
+#ifdef NAME_MAX
+ size += NAME_MAX; /* "best effort" :-/ */
+#elif defined(MAXNAMELEN)
+ size += MAXNAMELEN;
+#else
+ fprintf(stderr, "libHX-warning: Cannot determine buffer size for readdir\n");
+ closedir(tmp_dh);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if ((d = malloc(size)) == NULL) {
+ closedir(tmp_dh);
+ return NULL;
+ }
+ d->ptr = tmp_dh;
+#endif
+ return d;
+}
+
+EXPORT_SYMBOL const char *HXdir_read(struct HXdir *d)
+{
+ if (d == NULL)
+ return NULL;
+
+ errno = 0;
+#if defined _WIN32
+ if (!d->got_first) {
+ d->got_first = true;
+ if ((d->ptr = FindFirstFile(d->dname, &d->dentry)) == NULL)
+ return NULL;
+ } else if (!FindNextFile(d->ptr, &d->dentry)) {
+ return NULL;
+ }
+ return d->dentry.cFileName;
+#else
+ {
+ struct dirent *checkptr;
+ int i = readdir_r(d->ptr, &d->dentry, &checkptr);
+ if (checkptr == NULL || i < 0)
+ return NULL;
+ }
+ return d->dentry.d_name;
+#endif
+}
+
+EXPORT_SYMBOL void HXdir_close(struct HXdir *d)
+{
+ if (d == NULL)
+ return;
+#if defined _WIN32
+ FindClose(d->ptr);
+ free(d->dname);
+#else
+ closedir(d->ptr);
+#endif
+ free(d);
+}
+
+EXPORT_SYMBOL int HX_copy_file(const char *src, const char *dest,
+ unsigned int opts, ...)
+{
+ char buf[MAXLNLEN];
+ unsigned int extra_flags = 0;
+ int dd, eax = 0, sd, l;
+
+ if ((sd = open(src, O_RDONLY | O_BINARY)) < 0)
+ return -errno;
+ if (opts & HXF_KEEP)
+ extra_flags = O_EXCL;
+ dd = open(dest, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC |
+ extra_flags, S_IRUGO | S_IWUGO);
+ if (dd < 0) {
+ eax = errno;
+ close(sd);
+ errno = eax;
+ if (extra_flags != 0 && eax == EEXIST)
+ return 1;
+ return -errno;
+ }
+
+ while ((l = read(sd, buf, MAXLNLEN)) > 0 && write(dd, buf, l) > 0)
+ ;
+ close(sd);
+
+ if (opts & (HXF_UID | HXF_GID)) {
+ struct stat sb;
+ long uid, gid;
+ va_list argp;
+ va_start(argp, opts);
+
+ fstat(dd, &sb);
+ uid = sb.st_uid;
+ gid = sb.st_gid;
+
+ if (opts & HXF_UID) uid = va_arg(argp, long);
+ if (opts & HXF_GID) gid = va_arg(argp, long);
+ if (fchown(dd, uid, gid) < 0)
+ {};
+ va_end(argp);
+ }
+ close(dd);
+ return 1;
+}
+
+EXPORT_SYMBOL int HX_copy_dir(const char *src, const char *dest,
+ unsigned int opts, ...)
+{
+ void *dt = HXdir_open(src);
+ long uid = -1, gid = -1;
+ const char *fn;
+
+ if (dt == NULL)
+ return 0;
+
+ {
+ va_list argp;
+ va_start(argp, opts);
+ if (opts & HXF_UID) uid = va_arg(argp, long);
+ if (opts & HXF_GID) gid = va_arg(argp, long);
+ va_end(argp);
+ }
+
+ while ((fn = HXdir_read(dt)) != NULL) {
+ char fsrc[MAXFNLEN], fdest[MAXFNLEN];
+ struct stat sb;
+
+ if (strcmp(fn, ".") == 0 || strcmp(fn, "..") == 0)
+ continue;
+ snprintf(fsrc, MAXFNLEN, "%s/%s", src, fn);
+ snprintf(fdest, MAXFNLEN, "%s/%s", dest, fn);
+
+ lstat(fsrc, &sb);
+ sb.st_mode &= 0777; /* clear SUID/GUID/Sticky bits */
+
+ if (S_ISREG(sb.st_mode)) {
+ HX_copy_file(fsrc, fdest, opts, uid, gid);
+ } else if (S_ISDIR(sb.st_mode)) {
+ HX_mkdir(fdest, S_IRWXUGO);
+ HX_copy_dir(fsrc, fdest, opts, uid, gid);
+ } else if (S_ISLNK(sb.st_mode)) {
+ char pt[MAXFNLEN];
+ memset(pt, '\0', MAXFNLEN);
+ if (readlink(fsrc, pt, MAXFNLEN - 1) < MAXFNLEN - 1)
+ if (symlink(pt, fdest) < 0)
+ {};
+ } else if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
+ mknod(fdest, sb.st_mode, sb.st_dev);
+ } else if (S_ISFIFO(sb.st_mode)) {
+ mkfifo(fdest, sb.st_mode);
+ }
+
+ if (lchown(fdest, uid, gid) < 0)
+ {};
+ if (!S_ISLNK(sb.st_mode))
+ chmod(fdest, sb.st_mode);
+ }
+
+ HXdir_close(dt);
+ return 1;
+}
+
+EXPORT_SYMBOL int HX_mkdir(const char *idir, unsigned int mode)
+{
+ int i = 0, len = strlen(idir);
+ char buf[MAXFNLEN], dir[MAXFNLEN];
+
+#if defined(_WIN32)
+ {
+ char *p = dir;
+ HX_strlcpy(dir, idir, sizeof(dir));
+ for (; *p != '\0'; ++p)
+ if (*p == '\\')
+ *p = '/';
+ if (HX_isalpha(dir[0]) && dir[1] == ':')
+ i = 2;
+ }
+#else
+ HX_strlcpy(dir, idir, sizeof(dir));
+#endif
+
+ if (dir[i] == '/')
+ ++i;
+ for (; i < len; ++i) {
+ int v;
+ if (dir[i] == '/') {
+ strncpy(buf, dir, i);
+ buf[i] = '\0';
+ if ((v = mkdir_gen(buf, mode)) <= 0)
+ return v;
+ } else if (i == len - 1) {
+ strncpy(buf, dir, len);
+ buf[len] = '\0';
+ if ((v = mkdir_gen(buf, mode)) <= 0)
+ return v;
+ }
+ }
+ return 1;
+}
+
+/* Readlink - with a trailing zero (provided by HXmc) */
+EXPORT_SYMBOL int HX_readlink(hxmc_t **target, const char *path)
+{
+ bool dnull = *target == NULL;
+ char *tb;
+ int ret;
+
+ if (dnull) {
+ *target = HXmc_meminit(NULL, PATH_MAX);
+ if (*target == NULL)
+ return -errno;
+ }
+ tb = *target;
+ ret = readlink(path, tb, PATH_MAX);
+ if (ret < 0) {
+ ret = -errno;
+ if (!dnull) {
+ HXmc_free(*target);
+ *target = NULL;
+ }
+ return ret;
+ }
+ HXmc_setlen(target, ret);
+ return ret;
+}
+
+/**
+ * The buffers HX_realpath_symres are used are retained across symres calls to
+ * not do unnecessarily many allocation calls. Downside is that the state is
+ * roughly 12K in the worst case.
+ */
+struct HX_realpath_state {
+ hxmc_t *dest;
+ hxmc_t *link_target;
+ hxmc_t *new_path;
+ hxmc_t *symres_tmp;
+ const char *path;
+ unsigned int deref_count;
+};
+
+/**
+ * Perform symlink resolution on the currently last component (state->dest).
+ */
+static int
+HX_realpath_symres(struct HX_realpath_state *state, const char *path)
+{
+ int ret;
+
+ ret = HX_readlink(&state->link_target, state->dest);
+ if (ret == -EINVAL)
+ return -EINVAL;
+ else if (ret < 0)
+ return -errno;
+#ifdef __MINGW32__
+ else if (state->deref_count++ >= 40)
+ /*
+ * not that this is ever going to happen on mingw,
+ * because Windows does not seem to have symlinks, ...
+ */
+ return -EINVAL;
+#else
+ else if (state->deref_count++ >= 40)
+ return -ELOOP;
+#endif
+
+ if (*state->link_target == '/') {
+ *state->dest = '\0';
+ if (HXmc_setlen(&state->dest, 0) == NULL)
+ return -errno;
+ } else {
+ char *dptr = state->dest + HXmc_length(state->dest);
+ while (*--dptr != '/')
+ ;
+ *dptr = '\0';
+ if (HXmc_setlen(&state->dest, dptr - state->dest) == NULL)
+ return -errno;
+ }
+
+ if (HXmc_strcpy(&state->symres_tmp, state->link_target) == NULL)
+ return -errno;
+ /*
+ * @path could be pointing to @state->new_path already, so we need
+ * to construct the new path in a temp buffer (@symres_tmp) first.
+ */
+ if (HXmc_strcat(&state->symres_tmp, path) == NULL)
+ return -errno;
+ if (HXmc_strcpy(&state->new_path, state->symres_tmp) == NULL)
+ return -errno;
+ state->path = state->new_path;
+ return 1;
+}
+
+EXPORT_SYMBOL int HX_realpath(hxmc_t **dest_pptr, const char *path,
+ unsigned int flags)
+{
+ struct HX_realpath_state state = {.dest = *dest_pptr};
+ bool rq_slash = false, dnull = state.dest == NULL;
+ const char *cptr, *orig_path = path;
+ int ret = 0;
+
+ if (dnull) {
+ state.dest = HXmc_meminit(NULL, PATH_MAX);
+ if (state.dest == NULL)
+ goto err;
+ }
+
+ if (*path == '/') {
+ rq_slash = true;
+ } else if (flags & HX_REALPATH_ABSOLUTE) {
+ if (getcwd(state.dest, PATH_MAX) == NULL)
+ goto err;
+ rq_slash = true;
+ if (HXmc_setlen(&state.dest, strlen(state.dest)) == NULL)
+ goto err;
+ }
+
+ while (*path != '\0') {
+ if (*path == '/') {
+ ++path;
+ continue;
+ } else if (path[0] == '.' &&
+ (path[1] == '/' || path[1] == '\0') &&
+ flags & HX_REALPATH_SELF) {
+ ++path;
+ continue;
+ } else if (path[0] == '.' && path[1] == '.' &&
+ (path[2] == '/' || path[2] == '\0') &&
+ flags & HX_REALPATH_PARENT &&
+ ((flags & HX_REALPATH_ABSOLUTE) || *state.dest != '\0')) {
+ cptr = state.dest + HXmc_length(state.dest);
+ path += 2;
+ while (cptr > state.dest && *--cptr != '/')
+ ;
+ state.dest[cptr-state.dest] = '\0';
+ if (HXmc_setlen(&state.dest,
+ cptr - state.dest) == NULL)
+ goto err;
+ continue;
+ }
+
+ for (cptr = path; *cptr != '\0' && *cptr != '/'; ++cptr)
+ ;
+ if (rq_slash && HXmc_strcat(&state.dest, "/") == NULL)
+ goto out;
+ if (HXmc_memcat(&state.dest, path, cptr - path) == NULL)
+ goto out;
+ path = cptr;
+ rq_slash = true;
+
+ ret = HX_realpath_symres(&state, path);
+ if (ret == -EINVAL)
+ continue;
+ else if (ret < 0)
+ goto out;
+ path = state.path;
+ }
+
+ if (*state.dest == '\0') {
+ if (*orig_path == '/') {
+ if (HXmc_strcpy(&state.dest, "/") == NULL)
+ goto err;
+ } else {
+ if (HXmc_strcpy(&state.dest, ".") == NULL)
+ goto err;
+ }
+ }
+
+ *dest_pptr = state.dest;
+ HXmc_free(state.link_target);
+ HXmc_free(state.new_path);
+ HXmc_free(state.symres_tmp);
+ return 1;
+
+ err:
+ ret = -errno;
+ out:
+ if (dnull) {
+ /* If caller supplied a buffer, do not take it away. */
+ HXmc_free(state.dest);
+ *dest_pptr = NULL;
+ }
+ HXmc_free(state.link_target);
+ HXmc_free(state.new_path);
+ HXmc_free(state.symres_tmp);
+ return ret;
+}
+
+EXPORT_SYMBOL int HX_rrmdir(const char *dir)
+{
+ struct HXdir *ptr;
+ const char *trav;
+ hxmc_t *fn = NULL;
+ int ret = 0;
+
+ if ((ptr = HXdir_open(dir)) == NULL)
+ return -errno;
+
+ while ((trav = HXdir_read(ptr)) != NULL) {
+ struct stat sb;
+
+ if (strcmp(trav, ".") == 0 || strcmp(trav, "..") == 0)
+ continue;
+ HXmc_strcpy(&fn, dir);
+ HXmc_strcat(&fn, "/");
+ HXmc_strcat(&fn, trav);
+ if (lstat(fn, &sb) < 0) {
+ if (ret == 0)
+ ret = -errno;
+ continue;
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+ if (HX_rrmdir(fn) <= 0) {
+ if (ret == 0)
+ ret = -errno;
+ continue;
+ }
+ } else if (unlink(fn) < 0) {
+ if (ret == 0)
+ ret = -errno;
+ continue;
+ }
+ }
+
+ if (rmdir(dir) < 0) {
+ if (ret == 0)
+ ret = -errno;
+ }
+ HXdir_close(ptr);
+ HXmc_free(fn);
+ return ret;
+}
+
+EXPORT_SYMBOL ssize_t HXio_fullread(int fd, void *vbuf, size_t size)
+{
+ char *buf = vbuf;
+ size_t rem = size;
+ ssize_t ret;
+
+ while (rem > 0) {
+ ret = read(fd, buf, rem);
+ if (ret < 0)
+ return ret;
+ rem -= ret;
+ buf += ret;
+ }
+ return size;
+}
+
+EXPORT_SYMBOL ssize_t HXio_fullwrite(int fd, const void *vbuf, size_t size)
+{
+ const char *buf = vbuf;
+ size_t rem = size;
+ ssize_t ret;
+
+ while (rem > 0) {
+ ret = write(fd, buf, rem);
+ if (ret < 0)
+ return ret;
+ rem -= ret;
+ buf += ret;
+ }
+ return size;
+}
diff --git a/src/libHX.map b/src/libHX.map
new file mode 100644
index 0000000..ebd97b2
--- /dev/null
+++ b/src/libHX.map
@@ -0,0 +1,197 @@
+LIBHX_1.10.0 {
+global:
+ HX_basename;
+ HX_chomp;
+ HX_copy_dir;
+ HX_copy_file;
+ HX_dirname;
+ HX_dlclose;
+ HX_dlerror;
+ HX_dlopen;
+ HX_dlsym;
+ HX_ffs;
+ HX_getopt;
+ HX_getopt_help;
+ HX_getopt_usage;
+ HX_irand;
+ HX_rand;
+ HX_rrmdir;
+ HX_shconfig;
+ HX_shconfig_free;
+ HX_shconfig_pv;
+ HX_split5;
+ HX_split;
+ HX_strbchr;
+ HX_strclone;
+ HX_strlower;
+ HX_strltrim;
+ HX_strmid;
+ HX_strrcspn;
+ HX_strrev;
+ HX_strrtrim;
+ HX_strsep2;
+ HX_strsep;
+ HX_strupper;
+ HX_zvecfree;
+ HXdeque_del;
+ HXdeque_find;
+ HXdeque_free;
+ HXdeque_get;
+ HXdeque_init;
+ HXdeque_move;
+ HXdeque_pop;
+ HXdeque_push;
+ HXdeque_shift;
+ HXdeque_to_vec;
+ HXdeque_unshift;
+ HXdir_close;
+ HXdir_open;
+ HXdir_read;
+local:
+ *;
+};
+
+LIBHX_1.25 {
+global:
+ HX_getl;
+ HXmc_free;
+ HXmc_length;
+ HXmc_memcat;
+ HXmc_memcpy;
+ HXmc_memdel;
+ HXmc_meminit;
+ HXmc_memins;
+ HXmc_mempcat;
+ HXmc_strcat;
+ HXmc_strcpy;
+ HXmc_strinit;
+ HXmc_strins;
+ HXmc_strpcat;
+ HXmc_trunc;
+} LIBHX_1.10.0;
+
+LIBHX_1.26 {
+global:
+ HX_hexdump;
+ HX_time_compare;
+} LIBHX_1.25;
+
+LIBHX_2.0 {
+global:
+ HXmc_setlen;
+} LIBHX_1.26;
+
+LIBHX_2.2 {
+global:
+ HX_split4;
+ HXproc_run_async;
+ HXproc_run_sync;
+ HXproc_wait;
+} LIBHX_2.0;
+
+LIBHX_2.6 {
+global:
+ HX_fls;
+} LIBHX_2.2;
+
+LIBHX_2.9 {
+global:
+ HX_basename_exact;
+} LIBHX_2.6;
+
+LIBHX_3.0 {
+global:
+ HX_diff_timespec;
+ HX_diff_timeval;
+ HXformat_add;
+ HXformat_free;
+ HXformat_init;
+ HXhash_djb2;
+ HXhash_jlookup3;
+ HXhash_jlookup3s;
+ HXhash_primes;
+ HXmap_add;
+ HXmap_del;
+ HXmap_find;
+ HXmap_free;
+ HXmap_get;
+ HXmap_keysvalues;
+ HXmap_qfe;
+ HXmap_traverse;
+ HXmap_travfree;
+ HXmap_travinit;
+} LIBHX_2.9;
+
+LIBHX_3.2 {
+global:
+ HX_strquote;
+} LIBHX_3.0;
+
+LIBHX_3.3 {
+global:
+ HX_drand;
+ HX_shconfig_map;
+ HXdeque_genocide2;
+ HXmc_zvecfree;
+} LIBHX_3.2;
+
+LIBHX_3.4 {
+global:
+ HX_exit;
+ HX_init;
+ HX_memmem;
+} LIBHX_3.3;
+
+LIBHX_3.9 {
+global:
+ HXio_fullread;
+ HXio_fullwrite;
+} LIBHX_3.4;
+
+LIBHX_3.10 {
+global:
+ HX_readlink;
+ HX_realpath;
+} LIBHX_3.9;
+
+LIBHX_3.12 {
+global:
+ HX_getopt_help_cb;
+ HX_getopt_usage_cb;
+ HX_mkdir;
+ HX_strdup;
+ HX_strlcat;
+ HX_strlcpy;
+ HX_strlncat;
+ HX_strndup;
+ HX_strnlen;
+ HXformat_aprintf;
+ HXformat_fprintf;
+ HXformat_sprintf;
+ HXmap_init5;
+ HXmap_init;
+} LIBHX_3.10;
+
+# ABI 29
+LIBHX_3.13 {
+global:
+ HX_timespec_add;
+ HX_timespec_isneg;
+ HX_timespec_mul;
+ HX_timespec_mulf;
+ HX_timespec_neg;
+ HX_timespec_sub;
+ HX_timeval_sub;
+} LIBHX_3.12;
+
+# ABI 30
+LIBHX_3.15 {
+global:
+ HX_strchr2;
+} LIBHX_3.13;
+
+# ABI 31
+LIBHX_3.18 {
+global:
+ HX_stpltrim;
+} LIBHX_3.15;
diff --git a/src/map.c b/src/map.c
new file mode 100644
index 0000000..c1f332e
--- /dev/null
+++ b/src/map.c
@@ -0,0 +1,1414 @@
+/*
+ * Maps (key-value pairs)
+ * Copyright Jan Engelhardt, 2009
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ *
+ * Incorporates Public Domain code from Bob Jenkins's lookup3 (May 2006)
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libHX/list.h>
+#include <libHX/map.h>
+#include <libHX/string.h>
+#include "internal.h"
+#include "map_int.h"
+
+typedef void *(*clonefunc_t)(const void *, size_t);
+
+#ifdef NONPRIME_HASH
+/*
+ * If a hash function is good, it will yield an even distribution even with
+ * a non-prime-sized bucket set.
+ */
+EXPORT_SYMBOL const unsigned int HXhash_primes[] = {
+ 1 << 4, 1 << 5, 1 << 6, 1 << 7,
+ 1 << 8, 1 << 9, 1 << 10, 1 << 11,
+ 1 << 12, 1 << 13, 1 << 14, 1 << 15,
+ 1 << 16, 1 << 17, 1 << 18, 1 << 19,
+ 1 << 20, 1 << 21, 1 << 22, 1 << 23,
+ 1 << 24, 1 << 25, 1 << 26, 1 << 27,
+ 1 << 28, 1 << 29, 1 << 30, 1U << 31,
+};
+#else
+/*
+ * http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
+ * 23 and 3221.. added by j.eng.
+ */
+EXPORT_SYMBOL const unsigned int HXhash_primes[] = {
+ 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157,
+ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917,
+ 25165843, 50331653, 100663319, 201326611, 402653189, 805306457,
+ 1610612741, 3221225473U,
+};
+#endif
+
+static void HXhmap_free(struct HXhmap *hmap)
+{
+ struct HXhmap_node *drop, *dnext;
+ unsigned int i;
+
+ for (i = 0; i < HXhash_primes[hmap->power]; ++i) {
+ HXlist_for_each_entry_safe(drop, dnext,
+ &hmap->bk_array[i], anchor) {
+ if (hmap->super.ops.k_free != NULL)
+ hmap->super.ops.k_free(drop->key);
+ if (hmap->super.ops.d_free != NULL)
+ hmap->super.ops.d_free(drop->data);
+ free(drop);
+ }
+ }
+
+ free(hmap->bk_array);
+ free(hmap);
+}
+
+static void HXrbtree_free_dive(const struct HXrbtree *btree,
+ struct HXrbtree_node *node)
+{
+ /*
+ * Recursively dives into the tree and destroys elements. Note that you
+ * shall use this when destroying a complete tree instead of iterated
+ * deletion with HXrbtree_del(). Since this functions is meant to free
+ * it all, it does not need to care about rebalancing.
+ */
+ if (node->sub[RBT_LEFT] != NULL)
+ HXrbtree_free_dive(btree, node->sub[RBT_LEFT]);
+ if (node->sub[RBT_RIGHT] != NULL)
+ HXrbtree_free_dive(btree, node->sub[RBT_RIGHT]);
+ if (btree->super.ops.k_free != NULL)
+ btree->super.ops.k_free(node->key);
+ if (btree->super.ops.d_free != NULL)
+ btree->super.ops.d_free(node->data);
+ free(node);
+}
+
+static void HXrbtree_free(struct HXrbtree *btree)
+{
+ if (btree->root != NULL)
+ HXrbtree_free_dive(btree, btree->root);
+ free(btree);
+}
+
+EXPORT_SYMBOL void HXmap_free(struct HXmap *xmap)
+{
+ void *vmap = xmap;
+ const struct HXmap_private *map = vmap;
+
+ switch (map->type) {
+ case HXMAPT_HASH:
+ return HXhmap_free(vmap);
+ case HXMAPT_RBTREE:
+ return HXrbtree_free(vmap);
+ default:
+ break;
+ }
+}
+
+static int HXmap_valuecmp(const void *pa, const void *pb, size_t len)
+{
+ /*
+ * Cannot use "pa - pb" as that could underflow.
+ * Also, while "return (pa > pb) - (pa < pb)" does not use a branch,
+ * it compiles to more instructions and seems to be slower on x86.
+ */
+ return (pa > pb) ? 1 : (pa < pb) ? -1 : 0;
+}
+
+static void *HXmap_valuecpy(const void *p, size_t len)
+{
+ return const_cast1(void *, p);
+}
+
+#define jrot(x,k) (((x) << (k)) | ((x) >> (32 - (k))))
+
+EXPORT_SYMBOL unsigned long HXhash_jlookup3(const void *vkey, size_t length)
+{
+ static const unsigned int JHASH_GOLDEN_RATIO = 0x9e3779b9;
+ const uint8_t *key = vkey;
+ uint32_t a, b, c;
+
+ a = b = c = JHASH_GOLDEN_RATIO + length;
+ /* All but the last block: affect some 32 bits of (a,b,c) */
+ for (; length > 12; length -= 12, key += 12) {
+ a += key[0] + ((uint32_t)key[1] << 8) +
+ ((uint32_t)key[2] << 16) + ((uint32_t)key[3] << 24);
+ b += key[4] + ((uint32_t)key[5] << 8) +
+ ((uint32_t)key[6] << 16) + ((uint32_t)key[7] << 24);
+ c += key[8] + ((uint32_t)key[9] << 8) +
+ ((uint32_t)key[10] << 16)+ ((uint32_t)key[11] << 24);
+ /* jhash_mix - mix 3 32-bit values reversibly. */
+ a -= c; a ^= jrot(c, 4); c += b;
+ b -= a; b ^= jrot(a, 6); a += c;
+ c -= b; c ^= jrot(b, 8); b += a;
+ a -= c; a ^= jrot(c, 16); c += b;
+ b -= a; b ^= jrot(a, 19); a += c;
+ c -= b; c ^= jrot(b, 4); b += a;
+ }
+
+ switch (length) {
+ case 12: c += ((uint32_t)key[11]) << 24;
+ case 11: c += ((uint32_t)key[10]) << 16;
+ case 10: c += ((uint32_t)key[9]) << 8;
+ case 9: c += key[8];
+ case 8: b += ((uint32_t)key[7]) << 24;
+ case 7: b += ((uint32_t)key[6]) << 16;
+ case 6: b += ((uint32_t)key[5]) << 8;
+ case 5: b += key[4];
+ case 4: a += ((uint32_t)key[3]) << 24;
+ case 3: a += ((uint32_t)key[2]) << 16;
+ case 2: a += ((uint32_t)key[1]) << 8;
+ case 1: a += key[0];
+ break;
+ case 0: return c;
+ }
+ /* jhash_final */
+ c ^= b; c -= jrot(b, 14);
+ a ^= c; a -= jrot(c, 11);
+ b ^= a; b -= jrot(a, 25);
+ c ^= b; c -= jrot(b, 16);
+ a ^= c; a -= jrot(c, 4);
+ b ^= a; b -= jrot(a, 14);
+ c ^= b; c -= jrot(b, 24);
+ return c;
+}
+
+static unsigned long HXhash_jlookup3v(const void *p, size_t z)
+{
+ return HXhash_jlookup3(&p, sizeof(p));
+}
+
+EXPORT_SYMBOL unsigned long HXhash_jlookup3s(const void *p, size_t z)
+{
+ return HXhash_jlookup3(p, strlen(p));
+}
+
+EXPORT_SYMBOL unsigned long HXhash_djb2(const void *p, size_t z)
+{
+ const char *c = p;
+ unsigned long v = 5381;
+
+ while (*c != '\0')
+ v = ((v << 5) + v) ^ *c++;
+ /* v = v * 33 ^ *c++; */
+
+ return v;
+}
+
+/**
+ * Set up the operations for a map based on flags, and then override with
+ * user-specified functions.
+ */
+static void HXmap_ops_setup(struct HXmap_private *super,
+ const struct HXmap_ops *new_ops)
+{
+ struct HXmap_ops *ops = &super->ops;
+
+ ops->k_clone = HXmap_valuecpy;
+ ops->d_clone = HXmap_valuecpy;
+
+ if (super->flags & HXMAP_SKEY)
+ ops->k_compare = static_cast(void *, strcmp);
+ else if (super->key_size == 0)
+ ops->k_compare = HXmap_valuecmp;
+ else
+ ops->k_compare = memcmp;
+
+ if (super->flags & HXMAP_CKEY) {
+ ops->k_clone = (super->flags & HXMAP_SKEY) ?
+ reinterpret_cast(clonefunc_t, HX_strdup) :
+ HX_memdup;
+ ops->k_free = free;
+ }
+ if (super->flags & HXMAP_CDATA) {
+ ops->d_clone = (super->flags & HXMAP_SDATA) ?
+ reinterpret_cast(clonefunc_t, HX_strdup) :
+ HX_memdup;
+ ops->d_free = free;
+ }
+
+ if (super->type == HXMAPT_HASH) {
+ if (super->flags & HXMAP_SKEY)
+ ops->k_hash = HXhash_djb2;
+ else if (super->key_size != 0)
+ ops->k_hash = HXhash_jlookup3;
+ else
+ ops->k_hash = HXhash_jlookup3v;
+ }
+
+ if (new_ops == NULL)
+ return;
+
+ /* Update with user-supplied functions */
+ if (new_ops->k_compare != NULL)
+ ops->k_compare = new_ops->k_compare;
+ if (new_ops->k_clone != NULL)
+ ops->k_clone = new_ops->k_clone;
+ if (new_ops->k_free != NULL)
+ ops->k_free = new_ops->k_free;
+ if (new_ops->d_clone != NULL)
+ ops->d_clone = new_ops->d_clone;
+ if (new_ops->d_free != NULL)
+ ops->d_free = new_ops->d_free;
+ if (super->type == HXMAPT_HASH && new_ops->k_hash != NULL)
+ ops->k_hash = new_ops->k_hash;
+}
+
+/**
+ * @n: nominator of fraction
+ * @d: denominator of fraction
+ * @v: value
+ *
+ * Calculates @v * (@n / @d) without floating point or risk of overflow
+ * (when @n <= @d).
+ */
+static __inline__ unsigned int
+x_frac(unsigned int n, unsigned int d, unsigned int v)
+{
+ return (v / d) * n + (v % d) * n / d;
+}
+
+/**
+ * HXhmap_move - move elements from one map to another
+ * @bk_array: target bucket array
+ * @bk_number: number of buckets
+ * @hmap: old hash table
+ */
+static void HXhmap_move(struct HXlist_head *bk_array, unsigned int bk_number,
+ struct HXhmap *hmap)
+{
+ struct HXhmap_node *drop, *dnext;
+ unsigned int bk_idx, i;
+
+#ifdef NONPRIME_HASH
+ --bk_number;
+#endif
+ for (i = 0; i < HXhash_primes[hmap->power]; ++i)
+ HXlist_for_each_entry_safe(drop, dnext,
+ &hmap->bk_array[i], anchor) {
+#ifdef NONPRIME_HASH
+ bk_idx = hmap->super.ops.k_hash(drop->key,
+ hmap->super.key_size) & bk_number;
+#else
+ bk_idx = hmap->super.ops.k_hash(drop->key,
+ hmap->super.key_size) % bk_number;
+#endif
+ HXlist_del(&drop->anchor);
+ HXlist_add_tail(&bk_array[bk_idx], &drop->anchor);
+ }
+}
+
+/**
+ * HXhmap_layout - resize and rehash table
+ * @hmap: hash map
+ * @prime_idx: requested new table size (prime power thereof)
+ */
+static int HXhmap_layout(struct HXhmap *hmap, unsigned int power)
+{
+ const unsigned int bk_number = HXhash_primes[power];
+ struct HXlist_head *bk_array, *old_array = NULL;
+ unsigned int i;
+
+ bk_array = malloc(bk_number * sizeof(*bk_array));
+ if (bk_array == NULL)
+ return -errno;
+ for (i = 0; i < bk_number; ++i)
+ HXlist_init(&bk_array[i]);
+ if (hmap->bk_array != NULL) {
+ HXhmap_move(bk_array, bk_number, hmap);
+ old_array = hmap->bk_array;
+ /*
+ * It is ok to increment the TID this late. @map->bk_array is
+ * only emptied, and the new @bk_array is not yet visible to
+ * traversers, so no elements appear twice.
+ */
+ ++hmap->tid;
+ }
+ hmap->power = power;
+ hmap->min_load = (power != 0) ? HXhash_primes[power] / 4 : 0;
+ hmap->max_load = x_frac(7, 10, HXhash_primes[power]);
+ hmap->bk_array = bk_array;
+ free(old_array);
+ return 1;
+}
+
+static struct HXmap *HXhashmap_init4(unsigned int flags,
+ const struct HXmap_ops *ops, size_t key_size, size_t data_size)
+{
+ struct HXmap_private *super;
+ struct HXhmap *hmap;
+ int saved_errno;
+
+ if ((hmap = calloc(1, sizeof(*hmap))) == NULL)
+ return NULL;
+
+ super = &hmap->super;
+ super->flags = flags;
+ super->items = 0;
+ super->type = HXMAPT_HASH;
+ super->key_size = key_size;
+ super->data_size = data_size;
+ HXmap_ops_setup(super, ops);
+ hmap->tid = 1;
+ errno = HXhmap_layout(hmap, 0);
+ if (hmap->bk_array == NULL)
+ goto out;
+
+ errno = 0;
+ return static_cast(void *, hmap);
+
+ out:
+ saved_errno = errno;
+ HXhmap_free(hmap);
+ errno = saved_errno;
+ return NULL;
+}
+
+static struct HXmap *HXrbtree_init4(unsigned int flags,
+ const struct HXmap_ops *ops, size_t key_size, size_t data_size)
+{
+ struct HXmap_private *super;
+ struct HXrbtree *btree;
+
+ BUILD_BUG_ON(offsetof(struct HXrbtree, root) +
+ offsetof(struct HXrbtree_node, sub[0]) !=
+ offsetof(struct HXrbtree, root));
+
+ if ((btree = calloc(1, sizeof(*btree))) == NULL)
+ return NULL;
+
+ super = &btree->super;
+ super->type = HXMAPT_RBTREE;
+ super->flags = flags;
+ super->items = 0;
+ super->key_size = key_size;
+ super->data_size = data_size;
+ HXmap_ops_setup(super, ops);
+
+ /*
+ * TID must not be zero, otherwise the traverser functions will not
+ * start off correctly, since trav->tid is 0, but trav->tid must not
+ * equal btree->transact because that would mean the traverser is in
+ * sync with the tree.
+ */
+ btree->tid = 1;
+ btree->root = NULL;
+ return static_cast(void *, btree);
+}
+
+EXPORT_SYMBOL struct HXmap *HXmap_init5(enum HXmap_type type,
+ unsigned int flags, const struct HXmap_ops *ops, size_t key_size,
+ size_t data_size)
+{
+ if ((flags & HXMAP_SINGULAR) &&
+ (flags & (HXMAP_CDATA | HXMAP_SDATA) || data_size != 0))
+ fprintf(stderr, "WARNING: libHX-map: When HXMAP_SINGULAR is "
+ "set, HXMAP_CDATA, HXMAP_SDATA and/or data_size != 0 "
+ "has no effect.\n");
+
+ switch (type) {
+ case HXMAPT_HASH:
+ return HXhashmap_init4(flags, ops, key_size, data_size);
+ case HXMAPT_RBTREE:
+ return HXrbtree_init4(flags, ops, key_size, data_size);
+ default:
+ errno = -ENOENT;
+ return NULL;
+ }
+}
+
+EXPORT_SYMBOL struct HXmap *HXmap_init(enum HXmap_type type,
+ unsigned int flags)
+{
+ /*
+ * We cannot check this in HXmap_init5, since a custom ops may
+ * allow key_size==0/data_size==0.
+ */
+ if ((flags & HXMAP_SCKEY) == HXMAP_CKEY) {
+ fprintf(stderr, "%s: zero key_size with standard memcpy ops "
+ "won't work.\n", __func__);
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((flags & HXMAP_SCDATA) == HXMAP_CDATA) {
+ fprintf(stderr, "%s: zero data_size with standard memcpy ops "
+ "won't work.\n", __func__);
+ errno = EINVAL;
+ return NULL;
+ }
+ return HXmap_init5(type, flags, NULL, 0, 0);
+}
+
+static struct HXhmap_node *HXhmap_find(const struct HXhmap *hmap,
+ const void *key)
+{
+ struct HXhmap_node *drop;
+ unsigned int bk_idx;
+
+#ifdef NONPRIME_HASH
+ bk_idx = hmap->super.ops.k_hash(key, hmap->super.key_size) &
+ (HXhash_primes[hmap->power] - 1);
+#else
+ bk_idx = hmap->super.ops.k_hash(key, hmap->super.key_size) %
+ HXhash_primes[hmap->power];
+#endif
+ HXlist_for_each_entry(drop, &hmap->bk_array[bk_idx], anchor)
+ if (hmap->super.ops.k_compare(key, drop->key,
+ hmap->super.key_size) == 0)
+ return drop;
+ return NULL;
+}
+
+static const struct HXmap_node *HXrbtree_find(const struct HXrbtree *btree,
+ const void *key)
+{
+ struct HXrbtree_node *node = btree->root;
+ int res;
+
+ while (node != NULL) {
+ if ((res = btree->super.ops.k_compare(key,
+ node->key, btree->super.key_size)) == 0)
+ return static_cast(const void *, &node->key);
+ node = node->sub[res > 0];
+ }
+
+ return NULL;
+}
+
+EXPORT_SYMBOL const struct HXmap_node *
+HXmap_find(const struct HXmap *xmap, const void *key)
+{
+ const void *vmap = xmap;
+ const struct HXmap_private *map = vmap;
+
+ switch (map->type) {
+ case HXMAPT_HASH: {
+ const struct HXhmap_node *node = HXhmap_find(vmap, key);
+ if (node == NULL)
+ return NULL;
+ return static_cast(const void *, &node->key);
+ }
+ case HXMAPT_RBTREE:
+ return HXrbtree_find(vmap, key);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+EXPORT_SYMBOL void *HXmap_get(const struct HXmap *map, const void *key)
+{
+ const struct HXmap_node *node;
+
+ if ((node = HXmap_find(map, key)) == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+ errno = 0;
+ return node->data;
+}
+
+/**
+ * HXhmap_replace - replace value in a drop
+ */
+static int HXhmap_replace(const struct HXhmap *hmap, struct HXhmap_node *drop,
+ const void *value)
+{
+ void *old_value, *new_value;
+
+ if (hmap->super.flags & HXMAP_NOREPLACE)
+ return -EEXIST;
+
+ new_value = hmap->super.ops.d_clone(value, hmap->super.data_size);
+ if (new_value == NULL && value != NULL)
+ return -errno;
+ old_value = drop->data;
+ drop->data = new_value;
+ if (hmap->super.ops.d_free != NULL)
+ hmap->super.ops.d_free(old_value);
+ return 1;
+}
+
+static int HXhmap_add(struct HXhmap *hmap, const void *key, const void *value)
+{
+ struct HXhmap_node *drop;
+ unsigned int bk_idx;
+ int ret, saved_errno;
+
+ if ((drop = HXhmap_find(hmap, key)) != NULL)
+ return HXhmap_replace(hmap, drop, value);
+
+ if (hmap->super.items >= hmap->max_load &&
+ hmap->power < ARRAY_SIZE(HXhash_primes) - 1) {
+ if ((ret = HXhmap_layout(hmap, hmap->power + 1)) <= 0)
+ return ret;
+ } else if (hmap->super.items < hmap->min_load && hmap->power > 0) {
+ if ((ret = HXhmap_layout(hmap, hmap->power - 1)) <= 0)
+ return ret;
+ }
+
+ /* New node */
+ if ((drop = malloc(sizeof(*drop))) == NULL)
+ return -errno;
+ HXlist_init(&drop->anchor);
+ drop->key = hmap->super.ops.k_clone(key, hmap->super.key_size);
+ if (drop->key == NULL && key != NULL)
+ goto out;
+ drop->data = hmap->super.ops.d_clone(value, hmap->super.data_size);
+ if (drop->data == NULL && value != NULL)
+ goto out;
+
+#ifdef NONPRIME_HASH
+ bk_idx = hmap->super.ops.k_hash(key, hmap->super.key_size) &
+ (HXhash_primes[hmap->power] - 1);
+#else
+ bk_idx = hmap->super.ops.k_hash(key, hmap->super.key_size) %
+ HXhash_primes[hmap->power];
+#endif
+ HXlist_add_tail(&hmap->bk_array[bk_idx], &drop->anchor);
+ ++hmap->super.items;
+ return 1;
+
+ out:
+ saved_errno = errno;
+ if (hmap->super.ops.k_free != NULL)
+ hmap->super.ops.k_free(drop->key);
+ free(drop);
+ return -(errno = saved_errno);
+}
+
+/**
+ * HXrbtree_amov - do balance (move) after addition of a node
+ * @path: path from the root to the new node
+ * @dir: direction vectors
+ * @depth: current index in @path and @dir
+ * @tid: pointer to transaction ID which may need updating
+ */
+static void HXrbtree_amov(struct HXrbtree_node **path,
+ const unsigned char *dir, unsigned int depth, unsigned int *tid)
+{
+ struct HXrbtree_node *uncle, *parent, *grandp, *newnode;
+
+ /*
+ * The newly inserted node (or the last rebalanced node) at
+ * @path[depth-1] is red, so the parent must not be.
+ *
+ * Use an iterative approach to not waste time with recursive function
+ * calls. The @LR variable is used to handle the symmetric case without
+ * code duplication.
+ */
+ do {
+ unsigned int LR = dir[depth-2];
+
+ grandp = path[depth-2];
+ parent = path[depth-1];
+ uncle = grandp->sub[!LR];
+
+ if (uncle != NULL && uncle->color == RBT_RED) {
+ /*
+ * Case 3 (WP): Only colors have to be swapped to keep
+ * the black height. But rebalance needs to continue.
+ */
+ parent->color = RBT_BLACK;
+ uncle->color = RBT_BLACK;
+ grandp->color = RBT_RED;
+ depth -= 2;
+ continue;
+ }
+
+ /*
+ * Case 4 (WP): New node is the right child of its parent, and
+ * the parent is the left child of the grandparent. A left
+ * rotate is done at the parent to transform it into a case 5.
+ */
+ if (dir[depth-1] != LR) {
+ newnode = parent->sub[!LR];
+ parent->sub[!LR] = newnode->sub[LR];
+ newnode->sub[LR] = parent;
+ grandp->sub[LR] = newnode;
+ /* relabel */
+ parent = grandp->sub[LR];
+ /* unused assignment: newnode = parent->sub[LR]; */
+ } else {
+ /* unused assignment: newnode = path[depth]; */
+ }
+
+ /*
+ * Case 5: New node is the @LR child of its parent which is
+ * the @LR child of the grandparent. A right rotation on
+ * @grandp is performed.
+ */
+ grandp->sub[LR] = parent->sub[!LR];
+ parent->sub[!LR] = grandp;
+ path[depth-3]->sub[dir[depth-3]] = parent;
+ grandp->color = RBT_RED;
+ parent->color = RBT_BLACK;
+ ++*tid;
+ break;
+ } while (depth >= 3 && path[depth-1]->color == RBT_RED);
+}
+
+static int HXrbtree_replace(const struct HXrbtree *btree,
+ struct HXrbtree_node *node, const void *value)
+{
+ void *old_value, *new_value;
+
+ if (!(btree->super.flags & HXMAP_NOREPLACE))
+ return -(errno = EEXIST);
+
+ new_value = btree->super.ops.d_clone(value, btree->super.data_size);
+ if (new_value == NULL && value != NULL)
+ return -errno;
+ old_value = node->data;
+ node->data = new_value;
+ if (btree->super.ops.d_free != NULL)
+ btree->super.ops.d_free(old_value);
+ return 1;
+}
+
+static int HXrbtree_add(struct HXrbtree *btree,
+ const void *key, const void *value)
+{
+ struct HXrbtree_node *node, *path[RBT_MAXDEP];
+ unsigned char dir[RBT_MAXDEP];
+ unsigned int depth = 0;
+ int saved_errno;
+
+ /*
+ * Since our struct HXrbtree_node runs without a ->parent pointer,
+ * the path "upwards" from @node needs to be recorded somehow,
+ * here with @path. Another array, @dir is used to speedup direction
+ * decisions. (WP's "n->parent == grandparent(n)->left" is just slow.)
+ */
+ path[depth] = reinterpret_cast(struct HXrbtree_node *, &btree->root);
+ dir[depth++] = 0;
+ node = btree->root;
+
+ while (node != NULL) {
+ int res = btree->super.ops.k_compare(key,
+ node->key, btree->super.key_size);
+ if (res == 0)
+ /*
+ * The node already exists (found the key), overwrite
+ * the data.
+ */
+ return HXrbtree_replace(btree, node, value);
+
+ res = res > 0;
+ path[depth] = node;
+ dir[depth++] = res;
+ node = node->sub[res];
+ }
+
+ if ((node = malloc(sizeof(struct HXrbtree_node))) == NULL)
+ return -errno;
+
+ /* New node, push data into it */
+ node->key = btree->super.ops.k_clone(key, btree->super.key_size);
+ if (node->key == NULL && key != NULL)
+ goto out;
+ node->data = btree->super.ops.d_clone(value, btree->super.data_size);
+ if (node->data == NULL && value != NULL)
+ goto out;
+
+ /*
+ * Add the node to the tree. In trying not to hit a rule 2 violation
+ * (each simple path has the same number of black nodes), it is colored
+ * red so that below we only need to check for rule 1 violations.
+ */
+ node->sub[RBT_LEFT] = node->sub[RBT_RIGHT] = NULL;
+ node->color = RBT_RED;
+ path[depth-1]->sub[dir[depth-1]] = node;
+ ++btree->super.items;
+
+ /*
+ * WP: [[Red-black_tree]] says:
+ * Case 1: @node is root node - just color it black (see below).
+ * Case 2: @parent is black - no action needed (skip).
+ * No rebalance needed for a 2-node tree.
+ */
+ if (depth >= 3 && path[depth-1]->color == RBT_RED)
+ HXrbtree_amov(path, dir, depth, &btree->tid);
+
+ btree->root->color = RBT_BLACK;
+ return 1;
+
+ out:
+ saved_errno = errno;
+ if (btree->super.ops.k_free != NULL)
+ btree->super.ops.k_free(node->key);
+ if (btree->super.ops.d_free != NULL)
+ btree->super.ops.d_free(node->key);
+ free(node);
+ return -(errno = saved_errno);
+}
+
+EXPORT_SYMBOL int HXmap_add(struct HXmap *xmap,
+ const void *key, const void *value)
+{
+ void *vmap = xmap;
+ struct HXmap_private *map = vmap;
+
+ if ((map->flags & HXMAP_SINGULAR) && value != NULL) {
+ fprintf(stderr, "libHX-map: adding value!=NULL "
+ "into a set not allowed\n");
+ return -EINVAL;
+ }
+
+ switch (map->type) {
+ case HXMAPT_HASH:
+ return HXhmap_add(vmap, key, value);
+ case HXMAPT_RBTREE:
+ return HXrbtree_add(vmap, key, value);
+ default:
+ return -EINVAL;
+ }
+}
+
+static void *HXhmap_del(struct HXhmap *hmap, const void *key)
+{
+ struct HXhmap_node *drop;
+ void *value;
+
+ if ((drop = HXhmap_find(hmap, key)) == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ HXlist_del(&drop->anchor);
+ ++hmap->tid;
+ --hmap->super.items;
+ if (hmap->super.items < hmap->min_load && hmap->power > 0)
+ /*
+ * Ignore return value. If it failed, it will continue to use
+ * the current bk_array.
+ */
+ HXhmap_layout(hmap, hmap->power - 1);
+
+ value = drop->data;
+ if (hmap->super.ops.k_free != NULL)
+ hmap->super.ops.k_free(drop->key);
+ if (hmap->super.ops.d_free != NULL)
+ hmap->super.ops.d_free(drop->data);
+ free(drop);
+ errno = 0;
+ return value;
+}
+
+static unsigned int HXrbtree_del_mm(struct HXrbtree_node **path,
+ unsigned char *dir, unsigned int depth)
+{
+ /* Both subtrees exist */
+ struct HXrbtree_node *io_node, *io_parent, *orig_node = path[depth];
+ unsigned char color;
+ unsigned int spos;
+
+ io_node = orig_node->sub[RBT_RIGHT];
+ dir[depth] = RBT_RIGHT;
+
+ if (io_node->sub[RBT_LEFT] == NULL) {
+ /* Right subtree node is direct inorder */
+ io_node->sub[RBT_LEFT] = orig_node->sub[RBT_LEFT];
+ color = io_node->color;
+ io_node->color = orig_node->color;
+ orig_node->color = color;
+
+ path[depth-1]->sub[dir[depth-1]] = io_node;
+ path[depth++] = io_node;
+ return depth;
+ }
+
+ /*
+ * Walk down to the leftmost element, keep track of inorder node
+ * and its parent.
+ */
+ spos = depth++;
+
+ do {
+ io_parent = io_node;
+ path[depth] = io_parent;
+ dir[depth++] = RBT_LEFT;
+ io_node = io_parent->sub[RBT_LEFT];
+ } while (io_node->sub[RBT_LEFT] != NULL);
+
+ /* move node up */
+ path[spos-1]->sub[dir[spos-1]] = path[spos] = io_node;
+ io_parent->sub[RBT_LEFT] = io_node->sub[RBT_RIGHT];
+ io_node->sub[RBT_LEFT] = orig_node->sub[RBT_LEFT];
+ io_node->sub[RBT_RIGHT] = orig_node->sub[RBT_RIGHT];
+
+ color = io_node->color;
+ io_node->color = orig_node->color;
+
+ /*
+ * The nodes (@io_node and @orig_node) have been swapped. While
+ * @orig_node has no pointers to it, it still exists and decisions are
+ * made upon its properties in HXrbtree_del() and btree_dmov() until it
+ * is freed later. Hence we need to keep the color.
+ */
+ orig_node->color = color;
+ return depth;
+}
+
+static void HXrbtree_dmov(struct HXrbtree_node **path, unsigned char *dir,
+ unsigned int depth)
+{
+ struct HXrbtree_node *w, *x;
+
+ while (true) {
+ unsigned char LR = dir[depth - 1];
+ x = path[depth - 1]->sub[LR];
+
+ if (x != NULL && x->color == RBT_RED) {
+ /* (WP) "delete_one_child" */
+ x->color = RBT_BLACK;
+ break;
+ }
+
+ if (depth < 2)
+ /* Case 1 */
+ break;
+
+ /* @w is the sibling of @x (the current node). */
+ w = path[depth - 1]->sub[!LR];
+ if (w->color == RBT_RED) {
+ /*
+ * Case 2. @w is of color red. In order to collapse
+ * cases, a left rotate is performed at @x's parent and
+ * colors are swapped to make @w a black node.
+ */
+ w->color = RBT_BLACK;
+ path[depth - 1]->color = RBT_RED;
+ path[depth - 1]->sub[!LR] = w->sub[LR];
+ w->sub[LR] = path[depth - 1];
+ path[depth - 2]->sub[dir[depth - 2]] = w;
+ path[depth] = path[depth - 1];
+ dir[depth] = LR;
+ path[depth - 1] = w;
+ w = path[++depth - 1]->sub[!LR];
+ }
+
+ if ((w->sub[LR] == NULL || w->sub[LR]->color == RBT_BLACK) &&
+ (w->sub[!LR] == NULL || w->sub[!LR]->color == RBT_BLACK)) {
+ /* Case 3/4: @w has no red children. */
+ w->color = RBT_RED;
+ --depth;
+ continue;
+ }
+
+ if (w->sub[!LR] == NULL || w->sub[!LR]->color == RBT_BLACK) {
+ /* Case 5 */
+ struct HXrbtree_node *y = w->sub[LR];
+ y->color = RBT_BLACK;
+ w->color = RBT_RED;
+ w->sub[LR] = y->sub[!LR];
+ y->sub[!LR] = w;
+ w = path[depth - 1]->sub[!LR] = y;
+ }
+
+ /* Case 6 */
+ w->color = path[depth - 1]->color;
+ path[depth - 1]->color = RBT_BLACK;
+ w->sub[!LR]->color = RBT_BLACK;
+ path[depth - 1]->sub[!LR] = w->sub[LR];
+ w->sub[LR] = path[depth - 1];
+ path[depth - 2]->sub[dir[depth - 2]] = w;
+ break;
+ }
+}
+
+static void *HXrbtree_del(struct HXrbtree *btree, const void *key)
+{
+ struct HXrbtree_node *path[RBT_MAXDEP], *node;
+ unsigned char dir[RBT_MAXDEP];
+ unsigned int depth = 0;
+ void *itemptr;
+
+ if (btree->root == NULL)
+ return NULL;
+
+ path[depth] = reinterpret_cast(struct HXrbtree_node *, &btree->root);
+ dir[depth++] = 0;
+ node = btree->root;
+
+ while (node != NULL) {
+ int res = btree->super.ops.k_compare(key,
+ node->key, btree->super.key_size);
+ if (res == 0)
+ break;
+ res = res > 0;
+ path[depth] = node;
+ dir[depth++] = res;
+ node = node->sub[res];
+ }
+
+ if (node == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ /*
+ * Return the data for the node. But it is not going to be useful
+ * if ARBtree was directed to copy it (because it will be released
+ * below.)
+ */
+ itemptr = node->data;
+ /* Removal of the node from the tree */
+ --btree->super.items;
+ ++btree->tid;
+
+ path[depth] = node;
+ if (node->sub[RBT_RIGHT] == NULL)
+ /* Simple case: No right subtree, replace by left subtree. */
+ path[depth-1]->sub[dir[depth-1]] = node->sub[RBT_LEFT];
+ else if (node->sub[RBT_LEFT] == NULL)
+ /* Simple case: No left subtree, replace by right subtree. */
+ path[depth-1]->sub[dir[depth-1]] = node->sub[RBT_RIGHT];
+ else
+ /*
+ * Find minimum/maximum element in right/left subtree and
+ * do appropriate deletion while updating @path and @depth.
+ */
+ depth = HXrbtree_del_mm(path, dir, depth);
+
+ /*
+ * Deleting a red node does not violate either of the rules, so it is
+ * not necessary to rebalance in such a case.
+ */
+ if (node->color == RBT_BLACK)
+ HXrbtree_dmov(path, dir, depth);
+
+ if (btree->super.ops.k_free != NULL)
+ btree->super.ops.k_free(node->key);
+ if (btree->super.ops.d_free != NULL)
+ btree->super.ops.d_free(node->data);
+ free(node);
+ errno = 0;
+ /*
+ * In case %HXBT_CDATA was specified, the @itemptr value will be
+ * useless in most cases as it points to freed memory.
+ */
+ return itemptr;
+}
+
+EXPORT_SYMBOL void *HXmap_del(struct HXmap *xmap, const void *key)
+{
+ void *vmap = xmap;
+ struct HXmap_private *map = vmap;
+
+ switch (map->type) {
+ case HXMAPT_HASH:
+ return HXhmap_del(vmap, key);
+ case HXMAPT_RBTREE:
+ return HXrbtree_del(vmap, key);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+static void HXhmap_keysvalues(const struct HXhmap *hmap,
+ struct HXmap_node *array)
+{
+ const struct HXhmap_node *node;
+ unsigned int i;
+
+ for (i = 0; i < HXhash_primes[hmap->power]; ++i)
+ HXlist_for_each_entry(node, &hmap->bk_array[i], anchor) {
+ array->key = node->key;
+ array->data = node->data;
+ ++array;
+ }
+}
+
+static struct HXmap_node *HXrbtree_keysvalues(const struct HXrbtree_node *node,
+ struct HXmap_node *array)
+{
+ if (node->sub[0] != NULL)
+ array = HXrbtree_keysvalues(node->sub[0], array);
+ array->key = node->key;
+ array->data = node->data;
+ ++array;
+ if (node->sub[1] != NULL)
+ array = HXrbtree_keysvalues(node->sub[1], array);
+ return array;
+}
+
+EXPORT_SYMBOL struct HXmap_node *HXmap_keysvalues(const struct HXmap *xmap)
+{
+ const void *vmap = xmap;
+ const struct HXmap_private *map = vmap;
+ struct HXmap_node *array;
+
+ switch (map->type) {
+ case HXMAPT_HASH:
+ case HXMAPT_RBTREE:
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((array = malloc(sizeof(*array) * map->items)) == NULL)
+ return NULL;
+
+ switch (map->type) {
+ case HXMAPT_HASH:
+ HXhmap_keysvalues(vmap, array);
+ break;
+ case HXMAPT_RBTREE:
+ HXrbtree_keysvalues(
+ static_cast(const struct HXrbtree *, vmap)->root,
+ array);
+ break;
+ }
+ return array;
+}
+
+static void *HXhmap_travinit(const struct HXhmap *hmap, unsigned int flags)
+{
+ struct HXhmap_trav *trav;
+
+ if ((trav = malloc(sizeof(*trav))) == NULL)
+ return NULL;
+ /* We cannot offer DTRAV. */
+ trav->super.flags = flags & ~HXMAP_DTRAV;
+ trav->super.type = HXMAPT_HASH;
+ trav->hmap = hmap;
+ trav->head = NULL;
+ trav->bk_current = 0;
+ trav->tid = hmap->tid;
+ return trav;
+}
+
+static void *HXrbtrav_init(const struct HXrbtree *btree, unsigned int flags)
+{
+ struct HXrbtrav *trav;
+
+ if ((trav = calloc(1, sizeof(*trav))) == NULL)
+ return NULL;
+ trav->super.flags = flags;
+ trav->super.type = HXMAPT_RBTREE;
+ trav->tree = btree;
+ return trav;
+}
+
+EXPORT_SYMBOL struct HXmap_trav *HXmap_travinit(const struct HXmap *xmap,
+ unsigned int flags)
+{
+ const void *vmap = xmap;
+ const struct HXmap_private *map = vmap;
+
+ switch (map->type) {
+ case HXMAPT_HASH:
+ return HXhmap_travinit(vmap, flags);
+ case HXMAPT_RBTREE:
+ return HXrbtrav_init(vmap, flags);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+static const struct HXmap_node *HXhmap_traverse(struct HXhmap_trav *trav)
+{
+ const struct HXhmap *hmap = trav->hmap;
+ const struct HXhmap_node *drop;
+
+ if (trav->head == NULL) {
+ trav->head = hmap->bk_array[trav->bk_current].next;
+ } else if (trav->tid != hmap->tid) {
+ if (trav->bk_current >= HXhash_primes[hmap->power])
+ /* bk_array shrunk underneath us, we're done */
+ return NULL;
+ /*
+ * Reset head so that the while loop will be entered and we
+ * advance to the next bucket.
+ */
+ trav->head = &hmap->bk_array[trav->bk_current];
+ trav->tid = hmap->tid;
+ } else {
+ trav->head = trav->head->next;
+ }
+
+ while (trav->head == &hmap->bk_array[trav->bk_current]) {
+ if (++trav->bk_current >= HXhash_primes[hmap->power])
+ return false;
+ trav->head = hmap->bk_array[trav->bk_current].next;
+ }
+
+ drop = HXlist_entry(trav->head, struct HXhmap_node, anchor);
+ return static_cast(const void *, &drop->key);
+}
+
+static void HXrbtrav_checkpoint(struct HXrbtrav *trav,
+ const struct HXrbtree_node *node)
+{
+ const struct HXrbtree *tree = trav->tree;
+
+ if (tree->super.flags & HXMAP_DTRAV) {
+ void *old_key = trav->checkpoint;
+
+ trav->checkpoint = tree->super.ops.k_clone(node->key,
+ tree->super.key_size);
+ if (tree->super.ops.k_free != NULL)
+ tree->super.ops.k_free(old_key);
+ } else {
+ trav->checkpoint = node->key;
+ }
+}
+
+static struct HXrbtree_node *HXrbtrav_next(struct HXrbtrav *trav)
+{
+ if (trav->current->sub[RBT_RIGHT] != NULL) {
+ /* Got a right child */
+ struct HXrbtree_node *node;
+
+ trav->dir[trav->depth++] = RBT_RIGHT;
+ node = trav->current->sub[RBT_RIGHT];
+
+ /* Which might have left childs (our inorder successors!) */
+ while (node != NULL) {
+ trav->path[trav->depth] = node;
+ node = node->sub[RBT_LEFT];
+ trav->dir[trav->depth++] = RBT_LEFT;
+ }
+ trav->current = trav->path[--trav->depth];
+ } else if (trav->depth == 0) {
+ /* No right child, no more parents */
+ return trav->current = NULL;
+ } else if (trav->dir[trav->depth-1] == RBT_LEFT) {
+ /* We are the left child of the parent, move on to parent */
+ trav->current = trav->path[--trav->depth];
+ } else if (trav->dir[trav->depth-1] == RBT_RIGHT) {
+ /*
+ * There is no right child, and we are the right child of the
+ * parent, so move on to the next inorder node (a distant
+ * parent). This works by walking up the path until we are the
+ * left child of a parent.
+ */
+ while (true) {
+ if (trav->depth == 0)
+ /* No more parents */
+ return trav->current = NULL;
+ if (trav->dir[trav->depth-1] != RBT_RIGHT)
+ break;
+ --trav->depth;
+ }
+ trav->current = trav->path[--trav->depth];
+ }
+
+ HXrbtrav_checkpoint(trav, trav->current);
+ return trav->current;
+}
+
+static struct HXrbtree_node *HXrbtrav_rewalk(struct HXrbtrav *trav)
+{
+ /*
+ * When the binary tree has been distorted (or the traverser is
+ * uninitilaized), by either addition or deletion of an object, our
+ * path recorded so far is (probably) invalid too. rewalk() will go and
+ * find the node we were last at.
+ */
+ const struct HXrbtree *btree = trav->tree;
+ struct HXrbtree_node *node = btree->root;
+ bool go_next = false;
+
+ trav->depth = 0;
+
+ if (trav->current == NULL) {
+ /* Walk down the tree to the smallest element */
+ while (node != NULL) {
+ trav->path[trav->depth] = node;
+ node = node->sub[RBT_LEFT];
+ trav->dir[trav->depth++] = RBT_LEFT;
+ }
+ } else {
+ /* Search for the specific node to rebegin traversal at. */
+ const struct HXrbtree_node *newpath[RBT_MAXDEP];
+ unsigned char newdir[RBT_MAXDEP];
+ int newdepth = 0, res;
+ bool found = false;
+
+ while (node != NULL) {
+ newpath[newdepth] = trav->path[trav->depth] = node;
+ res = btree->super.ops.k_compare(trav->checkpoint,
+ node->key, btree->super.key_size);
+ if (res == 0) {
+ ++trav->depth;
+ found = true;
+ break;
+ }
+ res = res > 0;
+ trav->dir[trav->depth++] = res;
+
+ /*
+ * This (working) code gets 1st place in being totally
+ * cryptic without comments, so here goes:
+ *
+ * Right turns do not need to be saved, because we do
+ * not need to stop at that particular node again but
+ * can go directly to the next in-order successor,
+ * which must be a parent somewhere upwards where we
+ * did a left turn. If we only ever did right turns,
+ * we would be at the last node already.
+ *
+ * Imagine a 32-element perfect binary tree numbered
+ * from 1..32, and walk to 21 (directions: RLRL).
+ * The nodes stored are 24 and 22. btrav_next will
+ * go to 22, do 23, then jump _directly_ back to 24,
+ * omitting the redundant check at 20.
+ */
+ if (res == RBT_LEFT)
+ newdir[newdepth++] = RBT_LEFT;
+
+ node = node->sub[res];
+ }
+
+ if (found) {
+ /*
+ * We found the node, but which HXbtraverse() has
+ * already returned. Advance to the next inorder node.
+ * (Code needs to come after @current assignment.)
+ */
+ go_next = true;
+ } else {
+ /*
+ * If the node travp->current is actually deleted (@res
+ * will never be 0 above), traversal re-begins at the
+ * next inorder node, which happens to be the last node
+ * we turned left at.
+ */
+ memcpy(trav->path, newpath, sizeof(trav->path));
+ memcpy(trav->dir, newdir, sizeof(trav->dir));
+ trav->depth = newdepth;
+ }
+ }
+
+ if (trav->depth == 0) {
+ /* no more elements */
+ trav->current = NULL;
+ } else {
+ trav->current = trav->path[--trav->depth];
+ if (trav->current == NULL)
+ fprintf(stderr, "btrav_rewalk: problem: current==NULL\n");
+ HXrbtrav_checkpoint(trav, trav->current);
+ }
+
+ trav->tid = btree->tid;
+ if (go_next)
+ return HXrbtrav_next(trav);
+ else
+ return trav->current;
+}
+
+static const struct HXmap_node *HXrbtree_traverse(struct HXrbtrav *trav)
+{
+ const struct HXrbtree_node *node;
+
+ if (trav->tid != trav->tree->tid || trav->current == NULL)
+ /*
+ * Every HXrbtree operation that significantly changes the
+ * B-tree, increments @tid so we can decide here to rewalk.
+ */
+ node = HXrbtrav_rewalk(trav);
+ else
+ node = HXrbtrav_next(trav);
+
+ return (node != NULL) ? static_cast(const void *, &node->key) : NULL;
+}
+
+EXPORT_SYMBOL const struct HXmap_node *HXmap_traverse(struct HXmap_trav *trav)
+{
+ void *xtrav = trav;
+
+ if (xtrav == NULL)
+ return NULL;
+
+ switch (trav->type) {
+ case HXMAPT_HASH:
+ return HXhmap_traverse(xtrav);
+ case HXMAPT_RBTREE:
+ return HXrbtree_traverse(xtrav);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+static void HXrbtrav_free(struct HXrbtrav *trav)
+{
+ const struct HXmap_private *super = &trav->tree->super;
+
+ if ((super->flags & HXMAP_DTRAV) && super->ops.k_free != NULL)
+ super->ops.k_free(trav->checkpoint);
+ free(trav);
+}
+
+EXPORT_SYMBOL void HXmap_travfree(struct HXmap_trav *trav)
+{
+ void *xtrav = trav;
+
+ if (xtrav == NULL)
+ return;
+ switch (trav->type) {
+ case HXMAPT_RBTREE:
+ HXrbtrav_free(xtrav);
+ break;
+ default:
+ free(xtrav);
+ break;
+ }
+}
+
+static void HXhmap_qfe(const struct HXhmap *hmap, qfe_fn_t fn, void *arg)
+{
+ const struct HXhmap_node *hnode;
+ unsigned int i;
+
+ for (i = 0; i < HXhash_primes[hmap->power]; ++i)
+ HXlist_for_each_entry(hnode, &hmap->bk_array[i], anchor)
+ if (!(*fn)(static_cast(const void *, &hnode->key), arg))
+ return;
+}
+
+static void HXrbtree_qfe(const struct HXrbtree_node *node,
+ qfe_fn_t fn, void *arg)
+{
+ if (node->sub[RBT_LEFT] != NULL)
+ HXrbtree_qfe(node->sub[RBT_LEFT], fn, arg);
+ if (!(*fn)(static_cast(const void *, &node->key), arg))
+ return;
+ if (node->sub[RBT_RIGHT] != NULL)
+ HXrbtree_qfe(node->sub[RBT_RIGHT], fn, arg);
+}
+
+EXPORT_SYMBOL void HXmap_qfe(const struct HXmap *xmap, qfe_fn_t fn, void *arg)
+{
+ const void *vmap = xmap;
+ const struct HXmap_private *map = vmap;
+
+ switch (map->type) {
+ case HXMAPT_HASH:
+ HXhmap_qfe(vmap, fn, arg);
+ errno = 0;
+ break;
+ case HXMAPT_RBTREE: {
+ const struct HXrbtree *tree = vmap;
+ if (tree->root != NULL)
+ HXrbtree_qfe(tree->root, fn, arg);
+ errno = 0;
+ break;
+ }
+ default:
+ errno = EINVAL;
+ }
+}
diff --git a/src/map_int.h b/src/map_int.h
new file mode 100644
index 0000000..6fea432
--- /dev/null
+++ b/src/map_int.h
@@ -0,0 +1,128 @@
+#ifndef LIBHX_MAP_INTERNAL_H
+#define LIBHX_MAP_INTERNAL_H 1
+
+#include <libHX/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @type: actual type of map (%HX_MAPTYPE_*), used for virtual calls
+ * @ops: function pointers for key and data management
+ * @flags: bitfield of map flags
+ */
+struct HXmap_private {
+ /* from struct HXmap */
+ unsigned int items, flags;
+
+ /* private: */
+ enum HXmap_type type;
+ size_t key_size, data_size;
+ struct HXmap_ops ops;
+};
+
+/**
+ * @bk_array: bucket pointers
+ * @power: index into HXhash_primes to denote number of buckets
+ * @max_load: maximum number of elements before table gets enlarged
+ * @min_load: minimum number of elements before table gets shrunk
+ * @tid: transaction ID, used to track relayouts
+ */
+struct HXhmap {
+ struct HXmap_private super;
+
+ struct HXlist_head *bk_array;
+ unsigned int power, max_load, min_load, tid;
+};
+
+/**
+ * @anchor: anchor point in struct HXhmap_node
+ * @key: data that works as key
+ * @data: data that works as value
+ */
+struct HXhmap_node {
+ struct HXlist_head anchor;
+ /* HXmap_node */
+ union {
+ void *key;
+ const char *const skey;
+ };
+ union {
+ void *data;
+ char *sdata;
+ };
+};
+
+struct HXmap_trav {
+ enum HXmap_type type;
+ unsigned int flags;
+};
+
+struct HXhmap_trav {
+ struct HXmap_trav super;
+ const struct HXhmap *hmap;
+ const struct HXlist_head *head;
+ unsigned int bk_current, tid;
+};
+
+enum {
+ RBT_LEFT = 0,
+ RBT_RIGHT = 1,
+ RBT_RED = 0,
+ RBT_BLACK,
+ /* Allows for at least 16 million objects (in a worst-case tree) */
+ RBT_MAXDEP = 48,
+ /*
+ * Height h => pow(2, h/2)-1 nodes minimum. 1: 1, 2: 2, 4: 4, 6: 8,
+ * 8: 16, 10: 32, 12: 64, 14: 128, 16: 256, 18: 512, 20: 1K, 22: 2K,
+ * 24: 4K, 26: 8K, 28: 16K, 30: 32K, 32: 64K, 34: 128K, 36: 256K,
+ * 38: 512K, 40: 1M, 42: 2M, 44: 4M. 46: 8M, 48: 16M, 50: 32M, 52: 64M,
+ * 54: 128M, 56: 256M, 58: 512M, 60: 1G, 62: 2G, 64: 4G.
+ */
+};
+
+/**
+ * @sub: leaves
+ * @color: RBtree-specific node color
+ */
+struct HXrbtree_node {
+ struct HXrbtree_node *sub[2];
+ /* HXmap_node */
+ union {
+ void *key;
+ const char *const skey;
+ };
+ union {
+ void *data;
+ char *sdata;
+ };
+ unsigned char color;
+};
+
+struct HXrbtree {
+ struct HXmap_private super;
+ struct HXrbtree_node *root;
+ unsigned int tid;
+};
+
+struct HXrbtrav {
+ struct HXmap_trav super;
+ unsigned int tid; /* last seen btree transaction */
+ const struct HXrbtree *tree;
+ struct HXrbtree_node *current; /* last visited node */
+ char *checkpoint;
+ struct HXrbtree_node *path[RBT_MAXDEP]; /* stored path */
+ unsigned char dir[RBT_MAXDEP];
+ unsigned char depth;
+};
+
+typedef bool (*qfe_fn_t)(const struct HXmap_node *, void *);
+
+extern const unsigned int HXhash_primes[];
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LIBHX_MAP_INTERNAL_H */
diff --git a/src/mc.c b/src/mc.c
new file mode 100644
index 0000000..33500e7
--- /dev/null
+++ b/src/mc.c
@@ -0,0 +1,237 @@
+/*
+ * Auto-sizing memory containers
+ * Copyright Jan Engelhardt, 2002-2011
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libHX/string.h>
+#include "internal.h"
+
+static __inline__ size_t __HXmc_request(size_t len)
+{
+ /* The container, data portion, and a trailing \0 */
+ return sizeof(struct memcont) + len + 1;
+}
+
+static __inline__ void HXmc_check(const struct memcont *c)
+{
+ if (c->id != HXMC_IDENT)
+ fprintf(stderr, "libHX-mc error: not a hxmc object!\n");
+}
+
+static __inline__ struct memcont *HXmc_base(const hxmc_t *p)
+{
+ return containerof(p, struct memcont, data);
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_strinit(const char *s)
+{
+ hxmc_t *t = NULL;
+ size_t z = strlen(s);
+ if (z < 23 && HXmc_memcpy(&t, NULL, 23) == NULL)
+ return NULL;
+ return HXmc_memcpy(&t, s, z);
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_meminit(const void *ptr, size_t len)
+{
+ hxmc_t *t = NULL;
+ return HXmc_memcpy(&t, ptr, len);
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_strcpy(hxmc_t **vp, const char *s)
+{
+ if (s == NULL) {
+ HXmc_free(*vp);
+ *vp = NULL;
+ return NULL;
+ }
+ return HXmc_memcpy(vp, s, strlen(s));
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_memcpy(hxmc_t **vp, const void *ptr, size_t len)
+{
+ struct memcont *ctx;
+ if (*vp != NULL) {
+ ctx = HXmc_base(*vp);
+ HXmc_check(ctx);
+ if (ctx->alloc < len) {
+ ctx = realloc(ctx, __HXmc_request(len));
+ if (ctx == NULL)
+ return NULL;
+ ctx->alloc = len;
+ }
+ } else {
+ ctx = malloc(__HXmc_request(len));
+ if (ctx == NULL)
+ return NULL;
+ ctx->id = HXMC_IDENT;
+ ctx->alloc = len;
+ }
+
+ if (ptr == NULL) {
+ ctx->length = 0;
+ ctx->data[0] = '\0';
+ return *vp = ctx->data;
+ }
+
+ memcpy(ctx->data, ptr, ctx->length = len);
+ ctx->data[len] = '\0';
+ return *vp = ctx->data;
+}
+
+EXPORT_SYMBOL size_t HXmc_length(const hxmc_t *vp)
+{
+ const struct memcont *ctx;
+
+ if (vp == NULL)
+ return 0;
+ ctx = HXmc_base(vp);
+ HXmc_check(ctx);
+ return ctx->length;
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_setlen(hxmc_t **vp, size_t len)
+{
+ struct memcont *ctx;
+ if (HXmc_trunc(vp, len) == NULL)
+ return NULL;
+
+ ctx = HXmc_base(*vp);
+ ctx->length = len;
+ return *vp;
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_trunc(hxmc_t **vp, size_t len)
+{
+ struct memcont *ctx = HXmc_base(*vp);
+
+ HXmc_check(ctx);
+ if (len > ctx->alloc) {
+ ctx = realloc(ctx, __HXmc_request(len));
+ if (ctx == NULL)
+ return NULL;
+ ctx->alloc = len;
+ } else {
+ ctx->data[len] = '\0';
+ ctx->length = len;
+ }
+ return *vp = ctx->data;
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_strcat(hxmc_t **vp, const char *s)
+{
+ if (s == NULL)
+ return *vp;
+ return HXmc_memcat(vp, s, strlen(s));
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_memcat(hxmc_t **vp, const void *ptr, size_t len)
+{
+ struct memcont *ctx = HXmc_base(*vp);
+ size_t nl = ctx->length + len;
+
+ HXmc_check(ctx);
+ if (nl > ctx->alloc) {
+ ctx = realloc(ctx, __HXmc_request(nl));
+ if (ctx == NULL)
+ return NULL;
+ ctx->alloc = nl;
+ }
+ if (ptr == NULL)
+ return *vp = ctx->data;
+
+ memcpy(ctx->data + ctx->length, ptr, len);
+ ctx->length = nl;
+ ctx->data[nl] = '\0';
+ return *vp = ctx->data;
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_strpcat(hxmc_t **vp, const char *s)
+{
+ /* Prepend string @s to @*vp */
+ if (s == NULL)
+ return *vp;
+ return HXmc_memins(vp, 0, s, strlen(s));
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_mempcat(hxmc_t **vp, const void *ptr, size_t len)
+{
+ /* Prepend memory @ptr (of length @len) to @*vp */
+ return HXmc_memins(vp, 0, ptr, len);
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_strins(hxmc_t **vp, size_t pos, const char *s)
+{
+ if (s == NULL)
+ return *vp;
+ return HXmc_memins(vp, pos, s, strlen(s));
+}
+
+/*
+ * We naturally do not support negative positions like some
+ * scripting languages do, hence @pos is unsigned.
+ */
+
+EXPORT_SYMBOL hxmc_t *HXmc_memins(hxmc_t **vp, size_t pos, const void *ptr,
+ size_t len)
+{
+ struct memcont *ctx = HXmc_base(*vp);
+ size_t nl = ctx->length + len;
+
+ HXmc_check(ctx);
+ if (ctx->alloc < nl) {
+ ctx = realloc(ctx, __HXmc_request(nl));
+ if (ctx == NULL)
+ return NULL;
+ ctx->alloc = nl;
+ }
+ if (ptr == NULL)
+ return *vp = ctx->data;
+
+ memmove(ctx->data + pos + len, ctx->data + pos, ctx->length - pos);
+ memcpy(ctx->data + pos, ptr, len);
+ ctx->length += len;
+ ctx->data[ctx->length] = '\0';
+ return *vp = ctx->data;
+}
+
+EXPORT_SYMBOL hxmc_t *HXmc_memdel(hxmc_t *vp, size_t pos, size_t len)
+{
+ struct memcont *ctx = HXmc_base(vp);
+ HXmc_check(ctx);
+
+ if (pos + len > ctx->length)
+ len = ctx->length - pos;
+
+ memmove(ctx->data + pos, ctx->data + pos + len,
+ ctx->length - pos - len);
+ ctx->length -= len;
+ ctx->data[ctx->length] = '\0';
+ return ctx->data;
+}
+
+EXPORT_SYMBOL void HXmc_free(hxmc_t *vp)
+{
+ struct memcont *ctx;
+ if (vp == NULL)
+ return;
+ ctx = HXmc_base(vp);
+ HXmc_check(ctx);
+ free(ctx);
+}
+
+EXPORT_SYMBOL void HXmc_zvecfree(hxmc_t **args)
+{
+ hxmc_t **travp;
+ for (travp = args; *travp != NULL; ++travp)
+ HXmc_free(*travp);
+ free(args);
+}
diff --git a/src/misc.c b/src/misc.c
new file mode 100644
index 0000000..2eec3c2
--- /dev/null
+++ b/src/misc.c
@@ -0,0 +1,88 @@
+/*
+ * Miscellaneous functions
+ * Copyright Jan Engelhardt, 1999-2010
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libHX/ctype_helper.h>
+#include <libHX/misc.h>
+#include "internal.h"
+
+EXPORT_SYMBOL int HX_ffs(unsigned long n)
+{
+ int s = 0;
+ if (n == 0)
+ return -1;
+ while ((n >>= 1) >= 1)
+ ++s;
+ return s;
+}
+
+EXPORT_SYMBOL int HX_fls(unsigned long n)
+{
+ int i;
+ for (i = 31; i >= 0; --i)
+ if (n & (1 << i))
+ return i;
+ return -1;
+}
+
+static __inline__ void hexdump_ascii(FILE *fp, unsigned char c, bool tty)
+{
+ static const unsigned char ct_char[] = "31", up_char[] = "34";
+
+ if (HX_isprint(c))
+ fprintf(fp, "%c", c);
+ else if (tty && c == 0)
+ fprintf(fp, "\e[%sm@\e[0m", up_char); // ]]
+ else if (tty && c < 32)
+ fprintf(fp, "\e[%sm%c\e[0m", ct_char, '@' + c); // ]]
+ else if (tty)
+ fprintf(fp, "\e[%sm.\e[0m", up_char); // ]]
+ else
+ fprintf(fp, ".");
+}
+
+EXPORT_SYMBOL void HX_hexdump(FILE *fp, const void *vptr, unsigned int len)
+{
+ const unsigned char *ptr = vptr;
+ unsigned int i, j;
+ bool tty = isatty(fileno(fp));
+
+ fprintf(fp, "Dumping %u bytes\n", len);
+ for (i = 0; i < len / 16; ++i) {
+ fprintf(fp, "%04x | ", i * 16);
+ for (j = 0; j < 16; ++j)
+ fprintf(fp, "%02x%c", *ptr++, (j == 7) ? '-' : ' ');
+ ptr -= 16;
+ fprintf(fp, "| ");
+ for (j = 0; j < 16; ++j)
+ hexdump_ascii(fp, *ptr++, tty);
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "%04x | ", i * 16);
+ len -= i * 16;
+ for (i = 0; i < len; ++i)
+ fprintf(fp, "%02x%c", ptr[i], (i == 7) ? '-' : ' ');
+ for (; i < 16; ++i)
+ fprintf(fp, " ");
+ fprintf(fp, "| ");
+ for (i = 0; i < len; ++i)
+ hexdump_ascii(fp, *ptr++, tty);
+ fprintf(fp, "\n");
+}
+
+EXPORT_SYMBOL void HX_zvecfree(char **args)
+{
+ char **travp;
+ for (travp = args; *travp != NULL; ++travp)
+ free(*travp);
+ free(args);
+}
diff --git a/src/opt.c b/src/opt.c
new file mode 100644
index 0000000..0326f89
--- /dev/null
+++ b/src/opt.c
@@ -0,0 +1,986 @@
+/*
+ * libHX/opt.c
+ * Copyright Jan Engelhardt, 2002-2011
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libHX/ctype_helper.h>
+#include <libHX/deque.h>
+#include <libHX/map.h>
+#include <libHX/misc.h>
+#include <libHX/option.h>
+#include <libHX/string.h>
+#include "internal.h"
+
+/* Definitions */
+#define C_OPEN '('
+#define C_CLOSE ')'
+#define NTYPE_S(con, tpx) NTYPE((con), tpx, strtol)
+#define NTYPE_U(con, tpx) NTYPE((con), tpx, strtoul)
+
+#define NTYPE(con, tpx, func) case (con): { \
+ tpx *p, v = (func)(cbi->data, NULL, 0); \
+ if ((p = opt->ptr) != NULL) { \
+ if (opt->type & HXOPT_NOT) \
+ v = ~v; \
+ switch (opt->type & HXOPT_LOPMASK2) { \
+ case 0: *p = v; break; \
+ case HXOPT_OR: *p |= v; break; \
+ case HXOPT_AND: *p &= v; break; \
+ case HXOPT_XOR: *p ^= v; break; \
+ default: \
+ fprintf(stderr, "libHX-opt: illegal " \
+ "combination of logical op mask\n"); \
+ break; \
+ } \
+ } \
+ cbi->data_long = v; \
+ break; \
+}
+
+#define SCREEN_WIDTH 80 /* fine, popt also has it hardcoded */
+
+enum {
+
+ W_NONE = 0,
+ W_SPACE = 1 << 0,
+ W_BRACKET = 1 << 1,
+ W_ALT = 1 << 2,
+ W_EQUAL = 1 << 3,
+
+ HXOPT_LOPMASK2 = HXOPT_OR | HXOPT_AND | HXOPT_XOR,
+ HXOPT_LOPMASK = HXOPT_LOPMASK2 | HXOPT_NOT,
+ HXOPT_TYPEMASK = 0x1F, /* 5 bits */
+};
+
+/**
+ * HX_getopt_error - internal option parser error codes
+ * %HXOPT_E_LONG_UNKNOWN: unknown long option
+ * %HXOPT_E_LONG_TAKESVOID: long option was used with an arg (--long=arg)
+ * %HXOPT_E_LONG_MISSING: long option requires an argument
+ * %HXOPT_E_SHORT_UNKNOWN: unknown short option
+ * %HXOPT_E_SHORT_MISSING: short option requires an argument
+ */
+enum {
+ HXOPT_E_LONG_UNKNOWN = 1,
+ HXOPT_E_LONG_TAKESVOID,
+ HXOPT_E_LONG_MISSING,
+ HXOPT_E_SHORT_UNKNOWN,
+ HXOPT_E_SHORT_MISSING,
+};
+
+/**
+ * HX_getopt_state - internal option parser states
+ * %HXOPT_S_NORMAL: base state, options accepted
+ * %HXOPT_S_SHORT: a short option has been seen
+ * %HXOPT_S_TWOLONG: a long option has been seen
+ * %HXOPT_S_LONG: a long option and its argument have been seen
+ * %HXOPT_S_TERMINATED: options closed, all remaining args are to be copied
+ */
+enum HX_getopt_state {
+ HXOPT_S_NORMAL = 0,
+ HXOPT_S_SHORT,
+ HXOPT_S_TWOLONG,
+ HXOPT_S_LONG,
+ HXOPT_S_TERMINATED,
+};
+
+/**
+ * %HXOPT_I_ASSIGN: call do_assign
+ * %HXOPT_I_ADVARG: advance to next argument in @opt
+ * %HXOPT_I_ADVARG2: advance by two arguments in @opt
+ * %HXOPT_I_ADVCHAR: advance to next character in @cur
+ * %HXOPT_I_ERROR: HXoption error
+ */
+enum {
+ HXOPT_I_ASSIGN = 1 << 3,
+ HXOPT_I_ADVARG = 1 << 4,
+ HXOPT_I_ADVARG2 = 1 << 5,
+ HXOPT_I_ADVCHAR = 1 << 6,
+ HXOPT_I_ERROR = 1 << (sizeof(int) * CHAR_BIT - 2),
+
+ HXOPT_I_MASK = HXOPT_I_ADVARG | HXOPT_I_ADVARG2 | HXOPT_I_ADVCHAR |
+ HXOPT_I_ASSIGN | HXOPT_I_ERROR,
+};
+
+/**
+ * struct HX_getopt_vars - option parser working variable set
+ * @arg0: saved program name
+ * @remaining: list of extracted non-options
+ * @cbi: callback info
+ * @flags: flags setting the behavior for HX_getopt
+ */
+struct HX_getopt_vars {
+ const char *arg0;
+ struct HXdeque *remaining;
+ struct HXoptcb cbi;
+ unsigned int flags;
+};
+
+static bool posix_me_harder(void)
+{
+ const char *s;
+ char *end;
+ int res;
+
+ s = getenv("POSIXLY_CORRECT");
+ if (s == NULL || *s == '\0')
+ return false;
+ res = strtol(s, &end, 0);
+ if (end != s)
+ /* number */
+ return res;
+ return true; /* non-empty string */
+}
+
+static void do_assign(struct HXoptcb *cbi, const char *arg0)
+{
+ const struct HXoption *opt = cbi->current;
+
+ switch (opt->type & HXOPT_TYPEMASK) {
+ case HXTYPE_NONE: {
+ if (opt->ptr != NULL) {
+ int *p = opt->ptr;
+ if (opt->type & HXOPT_INC) ++*p;
+ else if (opt->type & HXOPT_DEC) --*p;
+ else *p = 1;
+ }
+ cbi->data_long = 1;
+ break;
+ }
+ case HXTYPE_VAL:
+ *static_cast(int *, opt->ptr) = cbi->data_long = opt->val;
+ break;
+ case HXTYPE_SVAL:
+ *static_cast(const char **, opt->ptr) = cbi->data = opt->uptr;
+ break;
+ case HXTYPE_BOOL: {
+ int *p;
+ if ((p = opt->ptr) != NULL)
+ *p = strcasecmp(cbi->data, "yes") == 0 ||
+ strcasecmp(cbi->data, "on") == 0 ||
+ strcasecmp(cbi->data, "true") == 0 ||
+ (HX_isdigit(*cbi->data) &&
+ strtoul(cbi->data, NULL, 0) != 0);
+ break;
+ }
+ case HXTYPE_BYTE:
+ *static_cast(unsigned char *, opt->ptr) = *cbi->data;
+ break;
+
+ NTYPE_U(HXTYPE_UCHAR, unsigned char);
+ NTYPE_S(HXTYPE_CHAR, char);
+ NTYPE_U(HXTYPE_USHORT, unsigned short);
+ NTYPE_S(HXTYPE_SHORT, short);
+ NTYPE_U(HXTYPE_UINT, unsigned int);
+ NTYPE_S(HXTYPE_INT, int);
+ NTYPE_U(HXTYPE_ULONG, unsigned long);
+ NTYPE_S(HXTYPE_LONG, long);
+ NTYPE_U(HXTYPE_UINT8, uint8_t);
+ NTYPE_S(HXTYPE_INT8, int8_t);
+ NTYPE_U(HXTYPE_UINT16, uint16_t);
+ NTYPE_S(HXTYPE_INT16, int16_t);
+ NTYPE_U(HXTYPE_UINT32, uint32_t);
+ NTYPE_S(HXTYPE_INT32, int32_t);
+#ifndef _MSC_VER
+ NTYPE(HXTYPE_ULLONG, unsigned long long, strtoull);
+ NTYPE(HXTYPE_LLONG, long long, strtoll);
+ NTYPE(HXTYPE_UINT64, uint64_t, strtoull);
+ NTYPE(HXTYPE_INT64, int64_t, strtoll);
+#endif
+ NTYPE(HXTYPE_SIZE_T, size_t, strtoull);
+ case HXTYPE_FLOAT:
+ cbi->data_dbl = strtod(cbi->data, NULL);
+ if (opt->ptr != NULL)
+ *static_cast(float *, opt->ptr) = cbi->data_dbl;
+ break;
+ case HXTYPE_DOUBLE:
+ cbi->data_dbl = strtod(cbi->data, NULL);
+ if (opt->ptr != NULL)
+ *static_cast(double *, opt->ptr) = cbi->data_dbl;
+ break;
+ case HXTYPE_STRING:
+ if (opt->ptr != NULL)
+ *static_cast(char **, opt->ptr) = HX_strdup(cbi->data);
+ break;
+ case HXTYPE_STRDQ:
+ HXdeque_push(opt->ptr, HX_strdup(cbi->data));
+ break;
+ case HXTYPE_MCSTR:
+ if (opt->ptr != NULL)
+ HXmc_strcpy(opt->ptr, cbi->data);
+ break;
+ case HXTYPE_XHELP:
+ cbi->data = arg0;
+ break;
+ default:
+ fprintf(stderr, "libHX-opt: illegal type %d\n",
+ opt->type & HXOPT_TYPEMASK);
+ break;
+ } /* switch */
+ if (opt->cb != NULL)
+ opt->cb(cbi);
+}
+
+static __inline__ const struct HXoption *
+lookup_short(const struct HXoption *table, char opt)
+{
+ for (; table->type != HXTYPE_XSNTMARK; ++table)
+ if (table->sh == opt)
+ return table;
+ return NULL;
+}
+
+static __inline__ const struct HXoption *
+lookup_long(const struct HXoption *table, const char *key)
+{
+ for (; table->type != HXTYPE_XSNTMARK; ++table)
+ if (table->ln != NULL && strcmp(table->ln, key) == 0)
+ return table;
+ return NULL;
+}
+
+static __inline__ bool takes_void(unsigned int t)
+{
+ t &= HXOPT_TYPEMASK;
+ return t == HXTYPE_NONE || t == HXTYPE_VAL || t == HXTYPE_SVAL ||
+ t == HXTYPE_XSNTMARK || t == HXTYPE_XHELP;
+}
+
+static void opt_to_text(const struct HXoption *opt, char *buf, size_t len,
+ unsigned int flags)
+{
+ const char *alt, *htyp = (opt->htyp != NULL) ? opt->htyp : "ARG";
+ size_t i = 0;
+ char equ;
+
+ if (flags & W_SPACE) buf[i++] = ' ';
+ if (flags & W_BRACKET) buf[i++] = '['; /* ] */
+ if (flags & W_ALT) {
+ alt = "|";
+ equ = (flags & W_EQUAL) ? '=' : ' ';
+ } else {
+ alt = ", ";
+ equ = '=';
+ }
+
+ if (opt->ln == NULL) {
+ buf[i++] = '-';
+ buf[i++] = opt->sh;
+ if (!takes_void(opt->type))
+ i += snprintf(buf + i, len - i, " %s", htyp);
+ } else {
+ if (opt->sh == '\0') {
+ if (takes_void(opt->type))
+ i += snprintf(buf + i, len - i,
+ "--%s", opt->ln);
+ else
+ i += snprintf(buf + i, len - i,
+ "--%s=%s", opt->ln, htyp);
+ } else {
+ if (takes_void(opt->type))
+ i += snprintf(buf + i, len - i, "-%c%s--%s",
+ opt->sh, alt, opt->ln);
+ else
+ i += snprintf(buf + i, len - i, "-%c%s--%s%c%s",
+ opt->sh, alt, opt->ln, equ, htyp);
+ }
+ }
+
+ if (flags & W_BRACKET)
+ buf[i++] = ']';
+ buf[i] = '\0';
+}
+
+static void print_indent(const char *msg, unsigned int ind, FILE *fp)
+{
+ size_t rest = SCREEN_WIDTH - ind;
+ char *p;
+
+ while (true) {
+ if (strlen(msg) < rest) {
+ fprintf(fp, "%s", msg);
+ break;
+ }
+ if ((p = HX_strbchr(msg, msg + rest, ' ')) == NULL) {
+ fprintf(fp, "%s", msg);
+ break;
+ }
+ fprintf(fp, "%.*s\n%*s", static_cast(unsigned int, p - msg),
+ msg, ind, "");
+ msg = p + 1;
+ rest = SCREEN_WIDTH - ind;
+ }
+ fprintf(fp, "\n");
+}
+
+/**
+ * HXparse_dequote_int - shell-style argument unescape
+ * @o: input/output string
+ * @end: terminating characters
+ *
+ * Unescapes a quoted argument, in-place.
+ * Returns a pointer to one position after the termination character.
+ */
+static char *HXparse_dequote_int(char *o, const char *end)
+{
+ char *i, quot = '\0';
+ for (i = o; *i != '\0'; ) {
+ if (quot == '\0') {
+ switch (*i) {
+ case '"':
+ case '\'':
+ quot = *i++;
+ continue;
+ case '\\':
+ if (*++i != '\0')
+ *o++ = *i++;
+ continue;
+ }
+ if (end != NULL && strchr(end, *i) != NULL) {
+ *o = '\0';
+ return i + 1;
+ }
+ *o++ = *i++;
+ continue;
+ }
+ if (*i == quot) {
+ quot = 0;
+ ++i;
+ continue;
+ } else if (*i == '\\') {
+ if (*++i != '\0')
+ *o++ = *i++;
+ continue;
+ }
+ *o++ = *i++;
+ }
+ *o = '\0';
+ return NULL;
+}
+
+/**
+ * HXparse_dequote_fmt
+ * @s: Input string
+ * @end: Terminating characters. May be %NULL.
+ * @pptr: Return pointer
+ *
+ * Dequote a string @s until @end, and return an allocated string that will
+ * contain the result, or %NULL on error. @*pptr will then point to the
+ * terminating character.
+ * Nested %() are honored.
+ *
+ * (This function is used from format.c. It is here in opt.c to call
+ * HXparse_dequote_int.)
+ */
+hxmc_t *HXparse_dequote_fmt(const char *s, const char *end, const char **pptr)
+{
+ unsigned int level = 0; /* nesting */
+ const char *i;
+ char quot = '\0';
+ hxmc_t *tmp;
+
+ /* Search for end */
+ for (i = s; *i != '\0'; ) {
+ if (quot == '\0') {
+ switch (*i) {
+ case '"':
+ case '\'':
+ quot = *i++;
+ continue;
+ case '\\':
+ if (i[1] != '\0')
+ i += 2;
+ continue;
+ case C_OPEN:
+ ++level;
+ ++i;
+ continue;
+ }
+ if (level == 0 && end != NULL &&
+ strchr(end, *i) != NULL)
+ break;
+ if (i[0] == C_CLOSE && level > 0)
+ --level;
+ ++i;
+ continue;
+ }
+ if (*i == quot) {
+ quot = 0;
+ ++i;
+ continue;
+ } else if (*i == '\\') {
+ if (*++i != '\0')
+ ++i;
+ continue;
+ }
+ ++i;
+ }
+
+ if (pptr != NULL)
+ *pptr = i;
+ tmp = HXmc_meminit(s, i - s);
+ if (tmp == NULL)
+ return NULL;
+ HXparse_dequote_int(tmp, NULL);
+ return tmp;
+}
+
+static int HX_getopt_error(int err, const char *key, unsigned int flags)
+{
+ switch (err) {
+ case HXOPT_E_LONG_UNKNOWN:
+ if (!(flags & HXOPT_QUIET))
+ fprintf(stderr, "Unknown option: %s\n", key);
+ return HXOPT_I_ERROR | HXOPT_ERR_UNKN;
+ case HXOPT_E_LONG_TAKESVOID:
+ if (!(flags & HXOPT_QUIET))
+ fprintf(stderr, "Option %s does not take "
+ "any argument\n", key);
+ return HXOPT_I_ERROR | HXOPT_ERR_VOID;
+ case HXOPT_E_LONG_MISSING:
+ if (!(flags & HXOPT_QUIET))
+ fprintf(stderr, "Option %s requires an "
+ "argument\n", key);
+ return HXOPT_I_ERROR | HXOPT_ERR_MIS;
+ case HXOPT_E_SHORT_UNKNOWN:
+ if (!(flags & HXOPT_QUIET))
+ fprintf(stderr, "Unknown option: -%c\n", *key);
+ return HXOPT_I_ERROR | HXOPT_ERR_UNKN;
+ case HXOPT_E_SHORT_MISSING:
+ if (!(flags & HXOPT_QUIET))
+ fprintf(stderr, "Option -%c requires an "
+ "argument\n", *key);
+ return HXOPT_I_ERROR | HXOPT_ERR_MIS;
+ }
+ return HXOPT_I_ERROR;
+}
+
+static int HX_getopt_twolong(const char *const *opt,
+ struct HX_getopt_vars *par)
+{
+ const char *key = opt[0], *value = opt[1];
+
+ par->cbi.current = lookup_long(par->cbi.table, key + 2);
+ if (par->cbi.current == NULL) {
+ if (par->flags & HXOPT_PTHRU) {
+ char *tmp = HX_strdup(key);
+ if (tmp == NULL)
+ return -errno;
+ if (HXdeque_push(par->remaining, tmp) == NULL) {
+ free(tmp);
+ return -errno;
+ }
+ return HXOPT_S_NORMAL | HXOPT_I_ADVARG;
+ }
+ return HX_getopt_error(HXOPT_E_LONG_UNKNOWN, key, par->flags);
+ }
+
+ par->cbi.flags = HXOPTCB_BY_LONG;
+ if (takes_void(par->cbi.current->type)) {
+ par->cbi.data = NULL;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG;
+ } else if (par->cbi.current->type & HXOPT_OPTIONAL) {
+ /* Rule: take arg if next thing is not-null, not-option. */
+ if (value == NULL || *value != '-' ||
+ (value[0] == '-' && value[1] == '\0')) {
+ /* --file -, --file bla */
+ par->cbi.data = value;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2;
+ } else {
+ /* --file --another --file -- endofoptions */
+ par->cbi.data = NULL;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG;
+ }
+ } else {
+ if (value == NULL)
+ return HX_getopt_error(HXOPT_E_LONG_MISSING, key, par->flags);
+ par->cbi.data = value;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2;
+ }
+}
+
+static int HX_getopt_long(const char *cur, struct HX_getopt_vars *par)
+{
+ int ret;
+ char *key, *value;
+
+ key = HX_strdup(cur);
+ if (key == NULL)
+ return -errno;
+
+ value = strchr(key, '=');
+ *value++ = '\0';
+ par->cbi.current = lookup_long(par->cbi.table, key + 2);
+ if (par->cbi.current == NULL) {
+ if (par->flags & HXOPT_PTHRU) {
+ /* Undo nuke of '=' and reuse alloc */
+ value[-1] = '=';
+ if (HXdeque_push(par->remaining, key) == NULL) {
+ free(key);
+ return -errno;
+ }
+ return HXOPT_S_NORMAL | HXOPT_I_ADVARG;
+ }
+ ret = HX_getopt_error(HXOPT_E_LONG_UNKNOWN, key, par->flags);
+ free(key);
+ return ret;
+ }
+ /*
+ * @value is always non-NULL when entering
+ * %HXOPT_S_LONG, so no need to check for !takes_void.
+ */
+ if (takes_void(par->cbi.current->type)) {
+ ret = HX_getopt_error(HXOPT_E_LONG_TAKESVOID, key, par->flags);
+ free(key);
+ return ret;
+ }
+
+ par->cbi.flags = HXOPTCB_BY_LONG;
+ par->cbi.data = value;
+ /* Not possible to use %HXOPT_I_ASSIGN due to transience of @key. */
+ do_assign(&par->cbi, par->arg0);
+ free(key);
+ return HXOPT_S_NORMAL | HXOPT_I_ADVARG;
+}
+
+static int HX_getopt_short(const char *const *opt, const char *cur,
+ struct HX_getopt_vars *par)
+{
+ char op = *cur;
+
+ if (op == '\0')
+ return HXOPT_S_NORMAL | HXOPT_I_ADVARG;
+
+ par->cbi.current = lookup_short(par->cbi.table, op);
+ if (par->cbi.current == NULL) {
+ if (par->flags & HXOPT_PTHRU) {
+ /*
+ * @cur-1 is always valid: it is either the previous
+ * char, or it is '-'.
+ */
+ char *buf = HX_strdup(cur - 1);
+ if (buf != NULL)
+ *buf = '-';
+ if (HXdeque_push(par->remaining, buf) == NULL) {
+ free(buf);
+ return -errno;
+ }
+ return HXOPT_S_NORMAL | HXOPT_I_ADVARG;
+ }
+ return HX_getopt_error(HXOPT_E_SHORT_UNKNOWN, &op, par->flags);
+ }
+
+ par->cbi.flags = HXOPTCB_BY_SHORT;
+ if (takes_void(par->cbi.current->type)) {
+ /* -A */
+ par->cbi.data = NULL;
+ return HXOPT_S_SHORT | HXOPT_I_ASSIGN | HXOPT_I_ADVCHAR;
+ } else if (cur[1] != '\0') {
+ /* -Avalue */
+ par->cbi.data = cur + 1;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG;
+ }
+
+ cur = *++opt;
+ if (par->cbi.current->type & HXOPT_OPTIONAL) {
+ if (cur == NULL || *cur != '-' ||
+ (cur[0] == '-' && cur[1] == '\0')) {
+ /* -f - -f bla */
+ par->cbi.data = cur;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2;
+ } else {
+ /* -f -a-file --another --file -- endofoptions */
+ par->cbi.data = NULL;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG;
+ }
+ } else {
+ /* -A value */
+ if (cur == NULL)
+ return HX_getopt_error(HXOPT_E_SHORT_MISSING, &op, par->flags);
+ par->cbi.data = cur;
+ return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2;
+ }
+}
+
+static int HX_getopt_term(const char *cur, const struct HX_getopt_vars *par)
+{
+ char *tmp = HX_strdup(cur);
+ if (tmp == NULL)
+ return -errno;
+ if (HXdeque_push(par->remaining, tmp) == NULL) {
+ free(tmp);
+ return -errno;
+ }
+ return HXOPT_S_TERMINATED | HXOPT_I_ADVARG;
+}
+
+static int HX_getopt_normal(const char *cur, const struct HX_getopt_vars *par)
+{
+ if (cur[0] == '-' && cur[1] == '\0') {
+ /* Note to popt developers: A single dash is NOT an option! */
+ HXdeque_push(par->remaining, HX_strdup(cur));
+ return HXOPT_S_NORMAL | HXOPT_I_ADVARG;
+ }
+ if (cur[0] == '-' && cur[1] == '-' && cur[2] == '\0') {
+ /*
+ * Double dash. If passthrough is on, "--" must be copied into
+ * @remaining. This is done in the next round.
+ */
+ if (!(par->flags & HXOPT_PTHRU))
+ return HXOPT_S_TERMINATED | HXOPT_I_ADVARG;
+ return HXOPT_S_TERMINATED;
+ }
+ if (cur[0] == '-' && cur[1] == '-') { /* long option */
+ if (strchr(cur + 2, '=') == NULL)
+ return HXOPT_S_TWOLONG;
+ /* Single argument long option: --long=arg */
+ return HXOPT_S_LONG;
+ }
+ if (cur[0] == '-')
+ /* Short option(s) - one or more(!) */
+ return HXOPT_S_SHORT | HXOPT_I_ADVCHAR;
+ if (par->flags & HXOPT_RQ_ORDER)
+ /* POSIX: first non-option implies option termination */
+ return HXOPT_S_TERMINATED;
+ cur = HX_strdup(cur);
+ if (cur == NULL || HXdeque_push(par->remaining, cur) == NULL)
+ return -errno;
+ return HXOPT_S_NORMAL | HXOPT_I_ADVARG;
+}
+
+EXPORT_SYMBOL int HX_getopt(const struct HXoption *table, int *argc,
+ const char ***argv, unsigned int flags)
+{
+ struct HX_getopt_vars ps;
+ const char **opt = *argv;
+ int state = HXOPT_S_NORMAL;
+ int ret = HXOPT_ERR_SUCCESS;
+ unsigned int argk;
+ const char *cur;
+
+ memset(&ps, 0, sizeof(ps));
+ ps.remaining = HXdeque_init();
+ if (ps.remaining == NULL)
+ goto out;
+ ps.flags = flags;
+ ps.arg0 = **argv;
+ ps.cbi.table = table;
+
+ if (*opt != NULL) {
+ /* put argv[0] back */
+ char *arg = HX_strdup(*opt++);
+ if (arg == NULL)
+ goto out_errno;
+ if (HXdeque_push(ps.remaining, arg) == NULL) {
+ free(arg);
+ goto out_errno;
+ }
+ }
+
+ if (posix_me_harder())
+ ps.flags |= HXOPT_RQ_ORDER;
+
+ for (cur = *opt; cur != NULL; ) {
+ if (state == HXOPT_S_TWOLONG)
+ state = HX_getopt_twolong(opt, &ps);
+ else if (state == HXOPT_S_LONG)
+ state = HX_getopt_long(cur, &ps);
+ else if (state == HXOPT_S_SHORT)
+ state = HX_getopt_short(opt, cur, &ps);
+ else if (state == HXOPT_S_TERMINATED)
+ state = HX_getopt_term(cur, &ps);
+ else if (state == HXOPT_S_NORMAL)
+ state = HX_getopt_normal(cur, &ps);
+
+ if (state < 0) {
+ ret = state;
+ break;
+ }
+ if (state & HXOPT_I_ERROR) {
+ ret = state & ~HXOPT_I_ERROR;
+ break;
+ }
+ if (state & HXOPT_I_ASSIGN)
+ do_assign(&ps.cbi, ps.arg0);
+ if (state & HXOPT_I_ADVARG)
+ cur = *++opt;
+ else if (state & HXOPT_I_ADVARG2)
+ cur = *(opt += 2);
+ else if (state & HXOPT_I_ADVCHAR)
+ ++cur;
+ state &= ~HXOPT_I_MASK;
+ }
+
+ out:
+ if (ret == HXOPT_ERR_SUCCESS) {
+ const char **nvec = reinterpret_cast(const char **,
+ HXdeque_to_vec(ps.remaining, &argk));
+ if (nvec == NULL)
+ goto out_errno;
+ if (ps.flags & HXOPT_DESTROY_OLD)
+ /*
+ * Only the "true, original" argv is stored on the
+ * stack - the argv that HX_getopt() produces is on
+ * the heap, so the %HXOPT_DESTROY_OLD flag should be
+ * passed when you use passthrough chaining, i.e. all
+ * but the first call to HX_getopt() should have this
+ * set.
+ */
+ HX_zvecfree(const_cast2(char **, *argv));
+
+ *argv = nvec;
+ if (argc != NULL)
+ *argc = argk;
+ } else if (ret < 0) {
+ if (!(ps.flags & HXOPT_QUIET))
+ fprintf(stderr, "%s: %s\n", __func__, strerror(errno));
+ } else {
+ ps.cbi.data = ps.arg0;
+ if (ps.flags & HXOPT_HELPONERR)
+ HX_getopt_help(&ps.cbi, stderr);
+ else if (ps.flags & HXOPT_USAGEONERR)
+ HX_getopt_usage(&ps.cbi, stderr);
+ }
+
+ HXdeque_free(ps.remaining);
+ return ret;
+
+ out_errno:
+ ret = -errno;
+ goto out;
+}
+
+EXPORT_SYMBOL void HX_getopt_help(const struct HXoptcb *cbi, FILE *nfp)
+{
+ FILE *fp = (nfp == NULL) ? stderr : nfp;
+ const struct HXoption *travp;
+ char tmp[84] = {'\0'};
+ unsigned int tw = 0;
+
+ HX_getopt_usage(cbi, nfp);
+
+ /* Find maximum indent */
+ for (travp = cbi->table; travp->type != HXTYPE_XSNTMARK; ++travp) {
+ size_t tl;
+
+ opt_to_text(travp, tmp, sizeof(tmp), W_EQUAL);
+ if ((tl = strlen(tmp)) > tw)
+ tw = tl;
+ }
+
+ /* Print table */
+ for (travp = cbi->table; travp->type != HXTYPE_XSNTMARK; ++travp) {
+ opt_to_text(travp, tmp, sizeof(tmp), W_NONE);
+ fprintf(fp, " %-*s ", static_cast(int, tw), tmp);
+ if (travp->help == NULL)
+ fprintf(fp, "\n");
+ else
+ print_indent(travp->help, tw + 6, fp);
+ }
+}
+
+EXPORT_SYMBOL void HX_getopt_help_cb(const struct HXoptcb *cbi)
+{
+ HX_getopt_help(cbi, stdout);
+ exit(EXIT_SUCCESS);
+}
+
+EXPORT_SYMBOL void HX_getopt_usage(const struct HXoptcb *cbi, FILE *nfp)
+{
+ size_t wd, tw = 0;
+ FILE *fp = (nfp == NULL) ? stderr : nfp;
+ const struct HXoption *travp;
+ char tmp[84] = {};
+ /* Program name now expected in .data */
+ const char *arg0 = cbi->data;
+
+ if (arg0 == NULL || *arg0 == '\0')
+ arg0 = "($0)";
+
+ wd = sizeof("Usage:") + strlen(arg0);
+ fprintf(fp, "Usage: %s", arg0);
+
+ /* Short-only flags */
+ if (wd + 5 > SCREEN_WIDTH) {
+ /* 5 is the minimum size for a new starting option, " [-X]" */
+ fprintf(fp, "\n ");
+ wd = 6;
+ }
+ for (travp = cbi->table; travp->type != HXTYPE_XSNTMARK; ++travp) {
+ if (!(travp->ln == NULL && travp->sh != '\0' &&
+ takes_void(travp->type)))
+ continue;
+ if (*tmp == '\0') {
+ snprintf(tmp, sizeof(tmp), " [-"); /* ] */
+ tw = 3;
+ }
+ tmp[tw++] = travp->sh;
+ if (wd + tw + 1 > SCREEN_WIDTH) {
+ tmp[tw++] = /* [ */ ']';
+ tmp[tw] = '\0';
+ fprintf(fp, "%s\n ", tmp);
+ wd = 6;
+ *tmp = '\0';
+ }
+ }
+ if (*tmp != '\0') {
+ tmp[tw++] = ']';
+ tmp[tw] = '\0';
+ wd += fprintf(fp, "%s", tmp);
+ }
+
+ /* Any other args */
+ for (travp = cbi->table; travp->type != HXTYPE_XSNTMARK; ++travp) {
+ if (travp->ln == NULL && travp->sh != '\0' &&
+ takes_void(travp->type))
+ continue;
+
+ opt_to_text(travp, tmp, sizeof(tmp),
+ W_SPACE | W_BRACKET | W_ALT);
+ if (wd + strlen(tmp) > SCREEN_WIDTH) {
+ fprintf(fp, "\n ");
+ wd = 6;
+ }
+ wd += fprintf(fp, "%s", tmp);
+ }
+
+ fprintf(fp, "\n");
+}
+
+EXPORT_SYMBOL void HX_getopt_usage_cb(const struct HXoptcb *cbi)
+{
+ HX_getopt_usage(cbi, stdout);
+ exit(EXIT_SUCCESS);
+}
+
+static void HX_shconf_break(void *ptr, char *line,
+ void (*cb)(void *, const char *, const char *))
+{
+ char *lp = line, *key, *val;
+ HX_chomp(line);
+
+ while (lp != NULL) {
+ while (HX_isspace(*lp) || *lp == ';')
+ ++lp;
+ /* Next entry if comment, empty line or no value */
+ if (*lp == '#' || *lp == '\0')
+ return;
+ if (!HX_isalpha(*lp) && *lp != '_')
+ /* Variables ought to start with [A-Z_] */
+ return;
+ key = lp;
+ while (HX_isalnum(*lp) || *lp == '_')
+ ++lp;
+ if (*lp != '=')
+ /* Variable name contained something not in [A-Z0-9_] */
+ return;
+ *lp++ = '\0';
+ val = lp;
+
+ /* Handle escape codes and quotes, and assign to TAB entry */
+ lp = HXparse_dequote_int(val, "\t\n ;");
+ (*cb)(ptr, key, val);
+ }
+}
+
+static void HX_shconf_assign(void *table, const char *key, const char *value)
+{
+ struct HXoptcb cbi = {
+ .table = table,
+ .flags = HXOPTCB_BY_LONG,
+ .data = value,
+ };
+
+ if ((cbi.current = lookup_long(table, key)) == NULL)
+ return;
+ do_assign(&cbi, NULL);
+}
+
+EXPORT_SYMBOL int HX_shconfig(const char *file, const struct HXoption *table)
+{
+ hxmc_t *ln = NULL;
+ FILE *fp;
+
+ if ((fp = fopen(file, "r")) == NULL)
+ return -errno;
+
+ while (HX_getl(&ln, fp) != NULL)
+ HX_shconf_break(const_cast(void *,
+ static_cast(const void *, table)), ln,
+ HX_shconf_assign);
+
+ HXmc_free(ln);
+ fclose(fp);
+ return 1;
+}
+
+static void HX_shconf_assignmp(void *map, const char *key, const char *value)
+{
+ HXmap_add(map, key, value);
+}
+
+EXPORT_SYMBOL struct HXmap *HX_shconfig_map(const char *file)
+{
+ struct HXmap *map;
+ hxmc_t *ln = NULL;
+ FILE *fp;
+
+ map = HXmap_init(HXMAPT_DEFAULT, HXMAP_SCKEY | HXMAP_SCDATA);
+ if (map == NULL)
+ return NULL;
+
+ if ((fp = fopen(file, "r")) == NULL) {
+ int saved_errno = errno;
+ HXmap_free(map);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ while (HX_getl(&ln, fp) != NULL)
+ HX_shconf_break(map, ln, HX_shconf_assignmp);
+
+ HXmc_free(ln);
+ fclose(fp);
+ return map;
+}
+
+EXPORT_SYMBOL int HX_shconfig_pv(const char **path, const char *file,
+ const struct HXoption *table, unsigned int flags)
+{
+ char buf[MAXFNLEN];
+ int ret = 0;
+
+ for (; *path != NULL; ++path) {
+ int v;
+ snprintf(buf, sizeof(buf), "%s/%s", *path, file);
+ v = HX_shconfig(buf, table);
+ if (v > 0) {
+ ++ret;
+ if (flags & SHCONF_ONE)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL void HX_shconfig_free(const struct HXoption *table)
+{
+ for (; table->ln != NULL; ++table) {
+ char **ptr = table->ptr;
+ if (table->type == HXTYPE_STRING &&
+ ptr != NULL && *ptr != NULL)
+ free(*ptr);
+ }
+}
diff --git a/src/proc.c b/src/proc.c
new file mode 100644
index 0000000..a7b47aa
--- /dev/null
+++ b/src/proc.c
@@ -0,0 +1,269 @@
+/*
+ * Process management
+ * Copyright Jan Engelhardt, 2008-2009
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include "config.h"
+#include "internal.h"
+
+#if !defined(HAVE_FORK) || !defined(HAVE_PIPE) || !defined(HAVE_EXECV) || \
+ !defined(HAVE_EXECVP)
+#include <errno.h>
+#include <libHX/proc.h>
+
+struct HXproc;
+
+EXPORT_SYMBOL int HXproc_run_async(const char *const *argv, struct HXproc *p)
+{
+ return -ENOSYS;
+}
+
+EXPORT_SYMBOL int HXproc_run_sync(const char *const *argv, unsigned int flags)
+{
+ /* Might use system() here... */
+ return -ENOSYS;
+}
+
+EXPORT_SYMBOL int HXproc_wait(struct HXproc *p)
+{
+ return -ENOSYS;
+}
+
+#else /* HAVE_FORK, HAVE_PIPE, HAVE_EXECVE */
+
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libHX/defs.h>
+#include <libHX/proc.h>
+#include "internal.h"
+
+#ifdef _WIN32
+# define NULL_DEVICE "nul"
+#else
+# define NULL_DEVICE "/dev/null"
+#endif
+
+/**
+ * HXproc_build_pipes -
+ * @fd_request: user array to tell us which pipe sets to create
+ * @p: result array
+ *
+ * Create some pipes.
+ * Explicitly initialize the @p array with -1 so that we can call close()
+ * on all of them without any side effects.
+ */
+static __inline__ int
+HXproc_build_pipes(const struct HXproc *proc, int (*p)[2])
+{
+ unsigned int x, y;
+
+ for (x = 0; x < 3; ++x)
+ for (y = 0; y < 2; ++y)
+ p[x][y] = -1;
+
+ if ((proc->p_flags & HXPROC_STDIN) && pipe(p[0]) < 0)
+ return -errno;
+ if ((proc->p_flags & HXPROC_STDOUT) && pipe(p[1]) < 0)
+ return -errno;
+ if ((proc->p_flags & HXPROC_STDERR) && pipe(p[2]) < 0)
+ return -errno;
+
+ return 1;
+}
+
+/**
+ * HXproc_close_pipes -
+ * @p: pipe fds to close
+ *
+ * In @p, there might be some fds that are -1 (due to the p[x][y] = -1 in
+ * HXproc_build_pipes()). That is ok, as closing -1 does not do anything.
+ */
+static void HXproc_close_pipes(int (*p)[2])
+{
+ if (p[0][0] >= 0)
+ close(p[0][0]);
+ if (p[0][1] >= 0)
+ close(p[0][1]);
+ if (p[1][0] >= 0)
+ close(p[1][0]);
+ if (p[1][1] >= 0)
+ close(p[1][1]);
+ if (p[2][0] >= 0)
+ close(p[2][0]);
+ if (p[2][1] >= 0)
+ close(p[2][1]);
+}
+
+/**
+ * HXproc_run_async -
+ * @argv: program and arguments
+ * @proc: control block with flags, also used to return info like fds
+ *
+ * Sets up pipes and runs the specified program.
+ */
+EXPORT_SYMBOL int
+HXproc_run_async(const char *const *argv, struct HXproc *proc)
+{
+ int pipes[3][2], nullfd = -1, ret, saved_errno;
+ unsigned int t;
+
+ if (argv == NULL || *argv == NULL)
+ return -EFAULT;
+
+ proc->p_stdin = proc->p_stdout = proc->p_stderr = -1;
+
+ t = (proc->p_flags & (HXPROC_STDIN | HXPROC_NULL_STDIN)) ==
+ (HXPROC_STDIN | HXPROC_NULL_STDIN);
+ t |= (proc->p_flags & (HXPROC_STDOUT | HXPROC_NULL_STDOUT)) ==
+ (HXPROC_STDOUT | HXPROC_NULL_STDOUT);
+ t |= (proc->p_flags & (HXPROC_STDERR | HXPROC_NULL_STDERR)) ==
+ (HXPROC_STDERR | HXPROC_NULL_STDERR);
+ if (t > 0)
+ return -EINVAL;
+
+ if (proc->p_flags & (HXPROC_NULL_STDIN | HXPROC_NULL_STDOUT |
+ HXPROC_NULL_STDERR)) {
+ if ((nullfd = open(NULL_DEVICE, O_RDWR)) < 0)
+ return -errno;
+ }
+ if ((ret = HXproc_build_pipes(proc, pipes)) <= 0) {
+ saved_errno = errno;
+ if (nullfd >= 0)
+ close(nullfd);
+ errno = saved_errno;
+ return ret;
+ }
+
+ if (proc->p_ops != NULL && proc->p_ops->p_prefork != NULL)
+ proc->p_ops->p_prefork(proc->p_data);
+ if ((proc->p_pid = fork()) < 0) {
+ saved_errno = errno;
+ if (proc->p_ops != NULL && proc->p_ops->p_complete != NULL)
+ proc->p_ops->p_complete(proc->p_data);
+ HXproc_close_pipes(pipes);
+ if (nullfd >= 0)
+ close(nullfd);
+ return -(errno = saved_errno);
+ } else if (proc->p_pid == 0) {
+ const char *prog = *argv;
+
+ /*
+ * Put file descriptors in place... and do so before postfork,
+ * as someone could have used proc.p_data = &proc; already.
+ *
+ * Take a dup of the pipe ends, so that close_pipes does not
+ * accidentally close them.
+ */
+ if (proc->p_flags & HXPROC_STDIN)
+ proc->p_stdin = dup(pipes[0][0]);
+ else if (proc->p_flags & HXPROC_NULL_STDIN)
+ proc->p_stdin = dup(nullfd);
+ if (proc->p_flags & HXPROC_STDOUT)
+ proc->p_stdout = dup(pipes[1][1]);
+ else if (proc->p_flags & HXPROC_NULL_STDOUT)
+ proc->p_stdout = dup(nullfd);
+ if (proc->p_flags & HXPROC_STDERR)
+ proc->p_stderr = dup(pipes[2][1]);
+ else if (proc->p_flags & HXPROC_NULL_STDERR)
+ proc->p_stderr = dup(nullfd);
+ if (proc->p_ops != NULL && proc->p_ops->p_postfork != NULL)
+ proc->p_ops->p_postfork(proc->p_data);
+
+ /*
+ * The rest of housekeeping. Now move the pipe ends onto
+ * their final fds.
+ */
+ HXproc_close_pipes(pipes);
+ if ((proc->p_flags & (HXPROC_STDIN | HXPROC_NULL_STDIN)) &&
+ proc->p_stdin != STDIN_FILENO) {
+ dup2(proc->p_stdin, STDIN_FILENO);
+ close(proc->p_stdin);
+ }
+ if ((proc->p_flags & (HXPROC_STDOUT | HXPROC_NULL_STDOUT)) &&
+ proc->p_stdout != STDOUT_FILENO) {
+ dup2(proc->p_stdout, STDOUT_FILENO);
+ close(proc->p_stdout);
+ }
+ if ((proc->p_flags & (HXPROC_STDERR | HXPROC_NULL_STDERR)) &&
+ proc->p_stderr != STDERR_FILENO) {
+ dup2(proc->p_stderr, STDERR_FILENO);
+ close(proc->p_stderr);
+ }
+ if (nullfd >= 0)
+ close(nullfd);
+ if (proc->p_flags & HXPROC_A0)
+ ++argv;
+ if (proc->p_flags & HXPROC_EXECV)
+ execv(prog, const_cast2(char * const *, argv));
+ else
+ execvp(prog, const_cast2(char * const *, argv));
+ if (proc->p_flags & HXPROC_VERBOSE)
+ fprintf(stderr, "%s: %s: %s\n", __func__,
+ prog, strerror(errno));
+ _exit(-1);
+ }
+
+ if (proc->p_flags & HXPROC_STDIN) {
+ close(pipes[0][0]);
+ proc->p_stdin = pipes[0][1];
+ }
+ if (proc->p_flags & HXPROC_STDOUT) {
+ close(pipes[1][1]);
+ proc->p_stdout = pipes[1][0];
+ }
+ if (proc->p_flags & HXPROC_STDERR) {
+ close(pipes[2][1]);
+ proc->p_stderr = pipes[2][0];
+ }
+
+ return 1;
+}
+
+EXPORT_SYMBOL int HXproc_run_sync(const char *const *argv, unsigned int flags)
+{
+ struct HXproc proc;
+ int ret;
+
+ memset(&proc, 0, sizeof(proc));
+ /*
+ * Assigning file descriptors makes no sense because they would not
+ * be read from. %HXPROC_NULL_* is ok of course.
+ */
+ if (flags & (HXPROC_STDIN | HXPROC_STDOUT | HXPROC_STDERR))
+ return -EINVAL;
+ proc.p_flags = flags;
+ if ((ret = HXproc_run_async(argv, &proc)) <= 0)
+ return ret;
+ return HXproc_wait(&proc);
+}
+
+EXPORT_SYMBOL int HXproc_wait(struct HXproc *proc)
+{
+ int status;
+
+ /* User has to close the pipes. Do not do it here. */
+
+ if (waitpid(proc->p_pid, &status, 0) < 0)
+ return -errno;
+ if (proc->p_ops != NULL && proc->p_ops->p_complete != NULL)
+ proc->p_ops->p_complete(proc->p_data);
+
+ if ((proc->p_exited = WIFEXITED(status)))
+ proc->p_status = WEXITSTATUS(status);
+ if ((proc->p_terminated = WIFSIGNALED(status)))
+ proc->p_status = WTERMSIG(status);
+ if (proc->p_terminated)
+ return static_cast(unsigned int,
+ static_cast(unsigned char, proc->p_status)) << 16;
+ return static_cast(unsigned char, proc->p_status);
+}
+
+#endif /* HAVE_lots */
diff --git a/src/rand.c b/src/rand.c
new file mode 100644
index 0000000..4a3f3ce
--- /dev/null
+++ b/src/rand.c
@@ -0,0 +1,123 @@
+/*
+ * Random numbers
+ * Copyright Jan Engelhardt, 2003-2008
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include "config.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#ifdef __unix__
+# include <unistd.h>
+#endif
+#ifdef _WIN32
+# include <process.h>
+#endif
+#include <libHX/init.h>
+#include <libHX/misc.h>
+#include "internal.h"
+
+static unsigned int HXrand_obtain_seed(void)
+{
+ unsigned int s;
+
+#if defined(HAVE_CLOCK_GETTIME)
+ struct timespec tv;
+
+ clock_gettime(CLOCK_REALTIME, &tv);
+ s = tv.tv_sec;
+ s ^= ~tv.tv_nsec;
+ clock_gettime(CLOCK_MONOTONIC, &tv);
+ s ^= tv.tv_sec;
+ s ^= ~tv.tv_nsec;
+#else
+ s = time(NULL);
+#endif
+#ifdef HAVE_GETPID
+ s ^= getpid() << 9;
+#endif
+#ifdef HAVE_GETPPID
+ s ^= getppid() << 1;
+#endif
+#ifdef HAVE_GETEUID
+ s ^= geteuid() << 13;
+#endif
+#ifdef HAVE_GETEGID
+ s ^= getegid() << 5;
+#endif
+ return s;
+}
+
+static void HXrand_init(void)
+{
+ unsigned int seed;
+ int fd, ret = 0;
+
+ if ((fd = open("/dev/urandom", O_RDONLY | O_BINARY)) >= 0) {
+ ret = read(fd, &seed, sizeof(seed));
+ close(fd);
+ }
+ if (ret != sizeof(seed))
+ seed = HXrand_obtain_seed();
+ srand(seed);
+}
+
+static pthread_mutex_t HX_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static unsigned long HX_use_count;
+
+EXPORT_SYMBOL int HX_init(void)
+{
+ pthread_mutex_lock(&HX_init_lock);
+ if (HX_use_count == 0)
+ HXrand_init();
+ ++HX_use_count;
+ pthread_mutex_unlock(&HX_init_lock);
+ return 1;
+}
+
+EXPORT_SYMBOL void HX_exit(void)
+{
+ pthread_mutex_lock(&HX_init_lock);
+ if (HX_use_count == 0)
+ fprintf(stderr, "%s: reference count is already zero!\n", __func__);
+ else
+ --HX_use_count;
+ pthread_mutex_unlock(&HX_init_lock);
+}
+
+EXPORT_SYMBOL int HX_rand(void)
+{
+ /*
+ * If there is an overly broken system, we may need to use
+ * alternate methods again (/dev/urandom?)
+ */
+ return rand();
+}
+
+EXPORT_SYMBOL double HX_drand(double lo, double hi)
+{
+ double delta = hi - lo;
+
+ return static_cast(double, rand()) * delta / RAND_MAX + lo;
+}
+
+EXPORT_SYMBOL unsigned int HX_irand(unsigned int lo, unsigned int hi)
+{
+ unsigned int delta = hi - lo;
+
+ if (delta == 0)
+ return lo;
+ else if (delta <= RAND_MAX)
+ return rand() % delta + lo;
+ else
+ return static_cast(unsigned int,
+ static_cast(double, rand()) * delta / RAND_MAX) + lo;
+}
diff --git a/src/rtcheck.c b/src/rtcheck.c
new file mode 100644
index 0000000..d9f623c
--- /dev/null
+++ b/src/rtcheck.c
@@ -0,0 +1,278 @@
+/*
+ * Additional runtime checks
+ * Copyright Jan Engelhardt, 2011
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ *
+ * libHX_rtcheck.so is a library supposed to be used together with the
+ * LD_PRELOAD environment variable to dynamically add extra checks.
+ */
+#define _GNU_SOURCE 1
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#ifdef RTLD_NEXT
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libHX.h>
+#include "internal.h"
+
+#define call_next(f) \
+ ((__typeof__(f) *)dlsym(RTLD_NEXT, #f))
+
+#define stub_head(f, args, invoke) \
+ EXPORT_SYMBOL __typeof__(f invoke) f args \
+ { \
+ if (HXrefchk_count <= 0) \
+ fprintf(stderr, "%s: HX_init has not been called!\n", \
+ __func__);
+
+#define stub_tail(f, params) \
+ return call_next(f) params; \
+ }
+
+#define stub(f, args, invoke, params) \
+ stub_head(f, args, invoke) \
+ stub_tail(f, params)
+
+#define stubv(f, args, params) \
+ EXPORT_SYMBOL void f args \
+ { \
+ if (HXrefchk_count <= 0) \
+ fprintf(stderr, "%s: HX_init has not been called!\n", \
+ __func__); \
+ call_next(f) params; \
+ }
+
+#define stub0(f) stub(f, (void), (), ())
+#define stub1(f, args) stub(f, args, (0), (a))
+#define stub1v(f, args) stubv(f, args, (a))
+#define stub2(f, args) stub(f, args, (0, 0), (a, b))
+#define stub2v(f, args) stubv(f, args, (a, b))
+#define stub3(f, args) stub(f, args, (0, 0, 0), (a, b, c))
+#define stub3v(f, args) stubv(f, args, (a, b, c))
+#define stub4(f, args) stub(f, args, (0, 0, 0, 0), (a, b, c, d))
+#define stub5(f, args) stub(f, args, (0, 0, 0, 0, 0), (a, b, c, d, e))
+
+static pthread_mutex_t HXrefchk_lock = PTHREAD_MUTEX_INITIALIZER;
+static unsigned long HXrefchk_count;
+
+EXPORT_SYMBOL int HX_init(void)
+{
+ /*
+ * The real HX_init has its own reference count check, but that
+ * variable is not exported that we could test it, so the counter needs
+ * to be replicated here.
+ */
+ pthread_mutex_lock(&HXrefchk_lock);
+ if (HXrefchk_count == 0) {
+ printf("# " PACKAGE_NAME " " PACKAGE_VERSION
+ " runtime checker active\n");
+ call_next(HX_init)();
+ }
+ ++HXrefchk_count;
+ pthread_mutex_unlock(&HXrefchk_lock);
+ return 1;
+}
+
+EXPORT_SYMBOL void HX_exit(void)
+{
+ pthread_mutex_lock(&HXrefchk_lock);
+ if (HXrefchk_count == 0)
+ fprintf(stderr, "%s: reference count is already zero!\n", __func__);
+ else
+ --HXrefchk_count;
+ pthread_mutex_unlock(&HXrefchk_lock);
+ call_next(HX_exit)();
+}
+
+/* deque.h */
+stub0(HXdeque_init);
+stub2(HXdeque_push, (struct HXdeque *a, const void *b));
+stub2(HXdeque_unshift, (struct HXdeque *a, const void *b));
+stub1(HXdeque_pop, (struct HXdeque *a));
+stub1(HXdeque_shift, (struct HXdeque *a));
+stub2(HXdeque_move, (struct HXdeque_node *a, struct HXdeque_node *b));
+stub2(HXdeque_find, (struct HXdeque *a, const void *b));
+stub2(HXdeque_get, (struct HXdeque *a, const void *b));
+stub1(HXdeque_del, (struct HXdeque_node *a));
+stub1v(HXdeque_free, (struct HXdeque *a));
+stub2v(HXdeque_genocide2, (struct HXdeque *a, void (*b)(void *)));
+stub2(HXdeque_to_vec, (const struct HXdeque *a, unsigned int *b));
+
+/* io.h */
+stub1(HXdir_open, (const char *a));
+stub1(HXdir_read, (struct HXdir *a));
+stub1v(HXdir_close, (struct HXdir *a));
+/* HX_copy_dir: has varargs */
+/* HX_copy_file: has varargs */
+stub2(HX_mkdir, (const char *a, unsigned int b));
+stub2(HX_readlink, (hxmc_t **a, const char *b));
+stub3(HX_realpath, (hxmc_t **a, const char *b, unsigned int c));
+stub1(HX_rrmdir, (const char *a));
+
+stub3(HXio_fullread, (int a, void *b, size_t c));
+stub3(HXio_fullwrite, (int a, const void *b, size_t c));
+
+/* map.h */
+stub2(HXmap_init, (enum HXmap_type a, unsigned int b));
+stub5(HXmap_init5, (enum HXmap_type a, unsigned int b,
+ const struct HXmap_ops *c, size_t d, size_t e));
+stub3(HXmap_add, (struct HXmap *a, const void *b, const void *c));
+stub2(HXmap_find, (const struct HXmap *a, const void *b));
+stub2(HXmap_get, (const struct HXmap *a, const void *b));
+stub2(HXmap_del, (struct HXmap *a, const void *b));
+stub1(HXmap_keysvalues, (const struct HXmap *a));
+stub2(HXmap_travinit, (const struct HXmap *a, unsigned int b));
+stub1(HXmap_traverse, (struct HXmap_trav *a));
+stub1v(HXmap_travfree, (struct HXmap_trav *a));
+stub3v(HXmap_qfe, (const struct HXmap *a,
+ bool (*b)(const struct HXmap_node *, void *), void *c));
+stub1v(HXmap_free, (struct HXmap *a));
+
+/* misc.h */
+stub1(HX_dlopen, (const char *a));
+stub2(HX_dlsym, (void *a, const char *b));
+stub1v(HX_dlclose, (void *a));
+stub0(HX_dlerror);
+
+stub1(HX_ffs, (unsigned long a));
+stub1(HX_fls, (unsigned long a));
+stub3(HX_hexdump, (FILE *a, const void *b, unsigned int c));
+stub3(HX_timespec_add, (struct timespec *a, const struct timespec *b,
+ const struct timespec *c));
+stub1(HX_timespec_isneg, (const struct timespec *a));
+stub3(HX_timespec_mul, (struct timespec *a, const struct timespec *b, int c));
+stub3(HX_timespec_mulf, (struct timespec *a, const struct timespec *b,
+ double c));
+stub2(HX_timespec_neg, (struct timespec *a, const struct timespec *b));
+stub3(HX_timespec_sub, (struct timespec *a, const struct timespec *b,
+ const struct timespec *c));
+stub3(HX_diff_timespec, (struct timespec *a, const struct timespec *b,
+ const struct timespec *c));
+stub3(HX_timeval_sub, (struct timeval *a, const struct timeval *b,
+ const struct timeval *c));
+stub3(HX_diff_timeval, (struct timeval *a, const struct timeval *b,
+ const struct timeval *c));
+stub3(HX_time_compare, (const struct stat *a, const struct stat *b, char c));
+stub1v(HX_zvecfree, (char **a));
+
+stub0(HX_rand);
+stub2(HX_irand, (unsigned int a, unsigned int b));
+stub2(HX_drand, (double a, double b));
+
+/* option.h */
+stub0(HXformat_init);
+stub1v(HXformat_free, (struct HXformat_map *a));
+stub4(HXformat_add, (struct HXformat_map *a, const char *b, const void *c,
+ unsigned int d));
+stub3(HXformat_aprintf, (const struct HXformat_map *a, hxmc_t **b,
+ const char *c));
+stub4(HXformat_sprintf, (const struct HXformat_map *a, char *b, size_t c,
+ const char *d));
+stub3(HXformat_fprintf, (const struct HXformat_map *a, FILE *b,
+ const char *c));
+
+stub4(HX_getopt, (const struct HXoption *a, int *b, const char ***c,
+ unsigned int d));
+/* HX_getopt_help: not really public */
+/* HX_getopt_help_cb: not really public */
+/* HX_getopt_usage: not really public */
+/* HX_getopt_usage_cb: not really public */
+stub2(HX_shconfig, (const char *a, const struct HXoption *b));
+stub1(HX_shconfig_map, (const char *a));
+stub4(HX_shconfig_pv, (const char **a, const char *b, const struct HXoption *c,
+ unsigned int d));
+stub1v(HX_shconfig_free, (const struct HXoption *a));
+
+/* proc.h */
+stub2(HXproc_run_async, (const char *const *a, struct HXproc *b));
+stub2(HXproc_run_sync, (const char *const *a, unsigned int b));
+stub1(HXproc_wait, (struct HXproc *a));
+
+/* string.h */
+static __inline__ struct memcont *HXmc_base(const hxmc_t *p)
+{
+ return containerof(p, struct memcont, data);
+}
+
+static __inline__ void HXmc_check(const char *func, const void *cv)
+{
+ const struct memcont *c;
+
+ if (cv == NULL)
+ return;
+ c = HXmc_base(cv);
+ if (c->id != HXMC_IDENT)
+ fprintf(stderr, "%s: %p is not a HXmc object!\n", func, cv);
+}
+
+stub1(HXmc_strinit, (const char *a));
+stub2(HXmc_meminit, (const void *a, size_t b));
+
+stub_head(HXmc_strcpy, (hxmc_t **a, const char *b), (0, 0))
+{
+ if (*a != NULL)
+ HXmc_check(__func__, *a);
+}
+stub_tail(HXmc_strcpy, (a, b))
+
+stub_head(HXmc_memcpy, (hxmc_t **a, const void *b, size_t c), (0, 0, 0))
+{
+ if (*a != NULL)
+ HXmc_check(__func__, *a);
+}
+stub_tail(HXmc_memcpy, (a, b, c))
+
+stub_head(HXmc_length, (const hxmc_t *a), (0))
+{
+ if (a != NULL)
+ HXmc_check(__func__, a);
+}
+stub_tail(HXmc_length, (a))
+
+stub2(HXmc_setlen, (hxmc_t **a, size_t b));
+stub2(HXmc_trunc, (hxmc_t **a, size_t b));
+stub2(HXmc_strcat, (hxmc_t **a, const char *b));
+stub3(HXmc_memcat, (hxmc_t **a, const void *b, size_t c));
+stub2(HXmc_strpcat, (hxmc_t **a, const char *b));
+stub3(HXmc_mempcat, (hxmc_t **a, const void *b, size_t c));
+stub3(HXmc_strins, (hxmc_t **a, size_t b, const char *c));
+stub4(HXmc_memins, (hxmc_t **a, size_t b, const void *c, size_t d));
+stub3(HXmc_memdel, (hxmc_t *a, size_t b, size_t c));
+stub1v(HXmc_free, (hxmc_t *a));
+stub1v(HXmc_zvecfree, (hxmc_t **a));
+
+stub1(HX_basename, (const char *a));
+stub1(HX_basename_exact, (const char *a));
+stub1(HX_chomp, (char *a));
+stub1(HX_dirname, (const char *a));
+stub2(HX_getl, (hxmc_t **a, FILE *b));
+stub4(HX_memmem, (const void *a, size_t b, const void *c, size_t d));
+stub4(HX_split, (const char *a, const char *b, int *c, int d));
+stub4(HX_split4, (char *a, const char *b, int *c, int d));
+stub4(HX_split5, (char *a, const char *b, int c, char **d));
+stub1(HX_stpltrim, (const char *a));
+stub1(HX_stprtrim, (char *a));
+stub3(HX_strbchr, (const char *a, const char *b, char c));
+stub2(HX_strclone, (char **a, const char *b));
+stub1(HX_strlower, (char *a));
+stub1(HX_strltrim, (char *a));
+stub3(HX_strmid, (const char *a, long b, long c));
+stub2(HX_strndup, (const char *a, size_t b));
+stub2(HX_strnlen, (const char *a, size_t b));
+stub3(HX_strquote, (const char *a, unsigned int b, char **c));
+stub2(HX_strrcspn, (const char *a, const char *b));
+stub1(HX_strrev, (char *a));
+stub1(HX_strrtrim, (char *a));
+stub2(HX_strsep, (char **a, const char *b));
+stub2(HX_strsep2, (char **a, const char *b));
+stub1(HX_strupper, (char *a));
+
+#endif /* RTLD_NEXT */
+#endif /* HAVE_DLFCN_H */
diff --git a/src/string.c b/src/string.c
new file mode 100644
index 0000000..2761276
--- /dev/null
+++ b/src/string.c
@@ -0,0 +1,836 @@
+/*
+ * C-string functions
+ * Copyright Jan Engelhardt, 1999-2010
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libHX/ctype_helper.h>
+#include <libHX/string.h>
+#include "internal.h"
+
+/**
+ * %HXQUOTE_ACCEPT: the listed characters are passed through,
+ * all others need to be quoted
+ * %HXQUOTE_REJECT: the listed characters need to be quoted,
+ * all others pass through
+ */
+enum HX_quote_selector {
+ HXQUOTE_ACCEPT,
+ HXQUOTE_REJECT,
+};
+
+/**
+ * @selector: whether this rule is accept- or reject-based
+ * @qchars: characters that need (no) quoting
+ */
+struct HX_quote_rule {
+ char selector;
+ const char *chars;
+};
+
+static const char HX_hexenc[16] = "0123456789ABCDEF";
+
+static __inline__ unsigned int min_uint(unsigned int a, unsigned int b)
+{
+ return (a < b) ? a : b;
+}
+
+EXPORT_SYMBOL char *HX_basename(const char *s)
+{
+ const char *p;
+ /* ignore trailing slashes */
+ for (p = s + strlen(s) - 1; p >= s && *p == '/'; --p)
+ ;
+ if (p < s)
+ /*
+ * String contained only slashes - this must be the root
+ * directory. Since we have the opportunity, rather than
+ * returning "////", just give the cleaned-up "/".
+ */
+ return const_cast1(char *, s + strlen(s) - 1);
+ if ((p = HX_strbchr(s, p, '/')) != NULL)
+ return const_cast1(char *, p + 1);
+ return const_cast1(char *, s);
+}
+
+EXPORT_SYMBOL char *HX_basename_exact(const char *s)
+{
+ const char *start, *end;
+ char *ret;
+ int len;
+
+ if (*s == '\0')
+ return HX_strdup(".");
+ /* ignore trailing slashes */
+ for (end = s + strlen(s) - 1; end >= s && *end == '/'; --end)
+ ;
+ if (end < s)
+ /* string consisted of only slashes */
+ return HX_strdup("/");
+
+ start = HX_strbchr(s, end, '/');
+ if (start == NULL) {
+ len = end - s + 1;
+ ret = HX_memdup(s, len + 1);
+ } else {
+ ++start;
+ len = end - start + 1;
+ ret = HX_memdup(start, len + 1);
+ }
+ if (ret == NULL)
+ return NULL;
+ ret[len] = '\0';
+ return ret;
+}
+
+EXPORT_SYMBOL char *HX_chomp(char *s)
+{
+ char *p = s + strlen(s) - 1;
+ while (p >= s) {
+ if (*p != '\n' && *p != '\r')
+ break;
+ *p-- = '\0';
+ }
+ return s;
+}
+
+EXPORT_SYMBOL char *HX_dirname(const char *s)
+{
+ const char *last, *stop;
+ char *p;
+
+ if (*s == '\0')
+ return HX_strdup(".");
+
+ for (last = s + strlen(s) - 1; last > s && *last == '/'; --last)
+ ;
+
+ if ((stop = HX_strbchr(s, last, '/')) == NULL)
+ return HX_strdup(".");
+
+ for (; stop > s && *stop == '/'; --stop)
+ ;
+
+ p = HX_memdup(s, stop - s + 2);
+ p[stop-s+1] = '\0';
+ return p;
+}
+
+EXPORT_SYMBOL hxmc_t *HX_getl(hxmc_t **ptr, FILE *fp)
+{
+ /* Read a whole line into a dynamic buffer. */
+ char temp[MAXLNLEN];
+
+ if (fgets(temp, sizeof(temp), fp) == NULL)
+ return NULL;
+
+ if (*ptr == NULL) {
+ *ptr = HXmc_meminit(NULL, 0);
+ if (*ptr == NULL)
+ return NULL;
+ } else {
+ HXmc_trunc(ptr, 0);
+ }
+
+ do {
+ if (HXmc_strcat(ptr, temp) == NULL)
+ return *ptr;
+ if (strchr(temp, '\n') != NULL)
+ break;
+ } while (fgets(temp, sizeof(temp), fp) != NULL);
+
+ return *ptr;
+}
+
+EXPORT_SYMBOL void *HX_memmem(const void *vspace, size_t spacesize,
+ const void *vpoint, size_t pointsize)
+{
+ const char *space = vspace, *point = vpoint;
+ const char *head, *end;
+ size_t tailsize;
+ char *tail;
+
+ if (pointsize == 0)
+ return const_cast1(void *, vspace);
+ if (pointsize > spacesize)
+ return NULL;
+
+ /* Do a BM-style trailer search and reduce calls to memcmp */
+ head = space + (pointsize - 1);
+ tail = memchr(head, point[pointsize-1], spacesize - (pointsize - 1));
+ if (tail == NULL || pointsize == 1)
+ return tail;
+ end = space + spacesize;
+ do {
+ head = tail - pointsize + 1;
+ if (memcmp(head, point, pointsize) == 0)
+ return const_cast(char *, head);
+ ++tail;
+ tailsize = end - tail;
+ tail = memchr(tail, point[pointsize-1], tailsize);
+ } while (tail != NULL);
+ return NULL;
+}
+
+EXPORT_SYMBOL char **HX_split(const char *str, const char *delim,
+ int *cp, int max)
+{
+ /*
+ * @countp can be NULL in case you are not interested in the number of
+ * fields. In either case, you can find out the number of fields by
+ * scanning through the resulting vector.
+ */
+ int count = 0;
+ char **ret;
+
+ if (cp == NULL)
+ cp = &count;
+ *cp = 1;
+
+ {
+ const char *wp = str;
+ while ((wp = strpbrk(wp, delim)) != NULL) {
+ if (++*cp >= max && max > 0) {
+ *cp = max;
+ break;
+ }
+ ++wp;
+ }
+ }
+
+ if (max == 0 || *cp < max)
+ max = *cp;
+ else if (*cp > max)
+ *cp = max;
+
+ ret = malloc(sizeof(char *) * (*cp + 1));
+ ret[*cp] = NULL;
+
+ {
+ char *seg, *wp = HX_strdup(str), *bg = wp;
+ size_t i = 0;
+
+ while (--max > 0) {
+ seg = HX_strsep(&wp, delim);
+ ret[i++] = HX_strdup(seg);
+ }
+
+ ret[i++] = HX_strdup(wp);
+ free(bg);
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL char **HX_split4(char *s, const char *delim, int *fld, int max)
+{
+ char **stk;
+ const char *p = s;
+ int count = 1;
+
+ for (p = strpbrk(p, delim); p != NULL; p = strpbrk(++p, delim))
+ if (++count >= max && max > 0) {
+ count = max;
+ break;
+ }
+
+ stk = malloc(sizeof(char *) * (count + 1));
+ if (stk == NULL)
+ return NULL;
+ stk[count] = NULL;
+ count = HX_split5(s, delim, count, stk);
+ if (fld != NULL)
+ *fld = count;
+ return stk;
+}
+
+EXPORT_SYMBOL int HX_split5(char *s, const char *delim, int max, char **stk)
+{
+ /*
+ * HX_split5 - the "stack split" (we try to avoid using the heap):
+ * Split @s (modifies it, so must be writable!) at @delim with at most
+ * @max fields and putting the results into @stk[0..@max-1].
+ *
+ * Example on @max:
+ * char *stk[max];
+ * HX_split5(s, delim, max, stk);
+ */
+ int i = 0;
+ char *p;
+
+ while (--max > 0) {
+ if ((p = strpbrk(s, delim)) == NULL)
+ break;
+ stk[i++] = s;
+ *p = '\0';
+ s = p + 1;
+ }
+
+ stk[i++] = s;
+ return i;
+}
+
+EXPORT_SYMBOL char *HX_strbchr(const char *start, const char *now, char d)
+{
+ /* Find the last occurrence of @d within @start and (including) @now. */
+ while (now >= start)
+ if (*now-- == d)
+ return const_cast1(char *, ++now);
+ return NULL;
+}
+
+/**
+ * This is the counterpart to strpbrk(). Returns a pointer to the first
+ * character not in @accept, or otherwise %NULL.
+ */
+EXPORT_SYMBOL char *HX_strchr2(const char *s, const char *accept)
+{
+ size_t seg = strspn(s, accept);
+
+ if (s[seg] == '\0')
+ return NULL;
+ return const_cast1(char *, s + seg);
+}
+
+EXPORT_SYMBOL char *HX_strclone(char **pa, const char *pb)
+{
+ if (*pa == pb)
+ return *pa;
+ if (*pa != NULL) {
+ free(*pa);
+ *pa = NULL;
+ }
+ if (pb == NULL)
+ return NULL;
+ if ((*pa = malloc(strlen(pb) + 1)) == NULL)
+ return NULL;
+ strcpy(*pa, pb);
+ return *pa;
+}
+
+EXPORT_SYMBOL char *HX_strdup(const char *src)
+{
+ if (src == NULL)
+ return NULL;
+ /* return HX_strndup(src, SIZE_MAX); */
+ return HX_memdup(src, strlen(src) + 1);
+}
+
+EXPORT_SYMBOL char *HX_strlcat(char *dest, const char *src, size_t len)
+{
+ ssize_t x = len - strlen(dest) - 1;
+ if (x <= 0)
+ return dest;
+ return strncat(dest, src, x);
+}
+
+EXPORT_SYMBOL char *HX_strlcpy(char *dest, const char *src, size_t n)
+{
+ strncpy(dest, src, n);
+ dest[n-1] = '\0';
+ return dest;
+}
+
+EXPORT_SYMBOL char *HX_strlncat(char *dest, const char *src, size_t dlen,
+ size_t slen)
+{
+ ssize_t x = dlen - strlen(dest) - 1;
+ if (x <= 0)
+ return dest;
+ x = ((ssize_t)slen < x) ? (ssize_t)slen : x;
+ return strncat(dest, src, x);
+}
+
+EXPORT_SYMBOL char *HX_strlower(char *orig)
+{
+ char *expr;
+ for (expr = orig; *expr != '\0'; ++expr)
+ *expr = HX_tolower(*expr);
+ return orig;
+}
+
+EXPORT_SYMBOL size_t HX_strltrim(char *expr)
+{
+ char *travp;
+ size_t diff = 0;
+ travp = expr;
+
+ while (HX_isspace(*travp))
+ ++travp;
+ if ((diff = travp - expr) > 0)
+ memmove(expr, travp, strlen(travp) + 1);
+ return diff;
+}
+
+EXPORT_SYMBOL char *HX_stpltrim(const char *p)
+{
+ while (HX_isspace(*p))
+ ++p;
+ return const_cast1(char *, p);
+}
+
+/* supports negative offsets like scripting languages */
+EXPORT_SYMBOL char *HX_strmid(const char *expr, long offset, long length)
+{
+ char *buffer;
+
+ if (offset < 0)
+ offset = strlen(expr) + offset;
+ if (length < 0)
+ length = strlen(expr) - offset + length;
+ if ((buffer = malloc(length + 1)) == NULL)
+ return NULL;
+
+ expr += offset;
+ return HX_strlcpy(buffer, expr, length + 1);
+}
+
+EXPORT_SYMBOL char *HX_strndup(const char *src, size_t size)
+{
+ char *ret;
+ size_t z;
+
+ if (src == NULL)
+ return NULL;
+ z = strlen(src);
+ if (z < size)
+ size = z;
+ if ((ret = malloc(size + 1)) == NULL)
+ return NULL;
+ memcpy(ret, src, size);
+ ret[size] = '\0';
+ return ret;
+}
+
+EXPORT_SYMBOL size_t HX_strnlen(const char *src, size_t size)
+{
+ const char *ptr = src;
+ for (; *ptr != '\0' && size > 0; --size, ++ptr)
+ ;
+ return ptr - src;
+}
+
+EXPORT_SYMBOL size_t HX_strrcspn(const char *s, const char *rej)
+{
+ size_t n = strlen(s);
+ const char *p = s + n;
+ while (--p >= s)
+ if (strchr(rej, *p) != NULL)
+ return p - s;
+ return n;
+}
+
+EXPORT_SYMBOL char *HX_strrev(char *s)
+{
+ size_t i, z = strlen(s)-1, z2 = z / 2;
+
+ for (i = 0; i < z2; ++i) {
+ char temp;
+ temp = s[i];
+ s[i] = s[z-i];
+ s[z-i] = temp;
+ }
+
+ return s;
+}
+
+EXPORT_SYMBOL size_t HX_strrtrim(char *expr)
+{
+ int i = strlen(expr), s = 0;
+ while (i-- && HX_isspace(expr[i]))
+ ++s;
+ expr[++i] = '\0';
+ return s;
+}
+
+EXPORT_SYMBOL char *HX_strsep(char **sp, const char *d)
+{
+ char *begin, *end;
+
+ if (*sp == NULL || **sp == '\0')
+ return NULL;
+ begin = *sp;
+
+ if (d[0] == '\0' || d[1] == '\0') {
+ if (*begin == *d)
+ end = begin;
+ else if (*begin == '\0')
+ end = NULL;
+ else
+ end = strchr(begin + 1, *d);
+ } else {
+ end = strpbrk(begin, d);
+ }
+
+ if (end == NULL) {
+ *sp = NULL;
+ } else {
+ *end++ = '\0';
+ *sp = end;
+ }
+
+ return begin;
+}
+
+EXPORT_SYMBOL char *HX_strsep2(char **wp, const char *str)
+{
+ char *ptr, *ret;
+ if (*wp == NULL)
+ return NULL;
+ ret = *wp;
+ if ((ptr = strstr(*wp, str)) == NULL) {
+ *wp = NULL;
+ return ret;
+ }
+ *ptr = '\0';
+ *wp = ptr + strlen(str);
+ return ret;
+}
+
+static const struct HX_quote_rule HX_quote_rules[] = {
+ [HXQUOTE_SQUOTE] = {HXQUOTE_REJECT, "'\\"},
+ [HXQUOTE_DQUOTE] = {HXQUOTE_REJECT, "\"\\"},
+ [HXQUOTE_HTML] = {HXQUOTE_REJECT, "\"&<>"},
+ [HXQUOTE_LDAPFLT] = {HXQUOTE_REJECT, "\n*()\\"},
+ [HXQUOTE_LDAPRDN] = {HXQUOTE_REJECT, "\n \"#+,;<=>\\"},
+ [HXQUOTE_URIENC] = {HXQUOTE_ACCEPT, "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
+ [HXQUOTE_SQLSQUOTE] = {HXQUOTE_REJECT, "'"},
+ [HXQUOTE_SQLBQUOTE] = {HXQUOTE_REJECT, "`"},
+};
+
+/**
+ * HX_qsize_bsa - calculate length of statically expanded string (for accepts)
+ * @s: input string
+ * @qchars: characters that need quoting
+ * @cost: quoting cost per quoted character
+ *
+ * The cost depends on the quote format. Typical values:
+ * 1 when "&" becomes "\&" (programming language-like)
+ * 2 when "&" becomes "\26" (LDAPRDN/HTTPURI-like hex encoding)
+ * 3 when "&" becomes "\x26" (hex encoding for programming)
+ */
+static size_t
+HX_qsize_bsa(const char *s, const char *qchars, unsigned int cost)
+{
+ const char *p = s;
+ size_t n = strlen(s);
+
+ while ((p = HX_strchr2(p, qchars)) != NULL) {
+ n += cost;
+ ++p;
+ }
+ return n;
+}
+
+/**
+ * HX_qsize_bsr - calculate length of statically expanded string (for rejects)
+ * @s: input string
+ * @qchars: characters that need quoting
+ * @cost: quoting cost per quoted character
+ *
+ * Same as for HX_qsize_bsa, but for HXQUOTE_REJECT-type rules.
+ */
+static size_t
+HX_qsize_bsr(const char *s, const char *qchars, unsigned int cost)
+{
+ const char *p = s;
+ size_t n = strlen(s);
+
+ while ((p = strpbrk(p, qchars)) != NULL) {
+ n += cost;
+ ++p;
+ }
+ return n;
+}
+
+static char *HX_quote_backslash(char *dest, const char *src, const char *qc)
+{
+ char *ret = dest;
+ size_t len;
+
+ while (*src != '\0') {
+ len = strcspn(src, qc);
+ if (len > 0) {
+ memcpy(dest, src, len);
+ dest += len;
+ src += len;
+ if (*src == '\0')
+ break;
+ }
+ *dest++ = '\\';
+ *dest++ = *src++;
+ }
+
+ *dest = '\0';
+ return ret;
+}
+
+static char *
+HX_quote_sqlbackslash(char *dest, const char *src, const char *trm)
+{
+ char *ret = dest;
+ size_t len;
+
+ while (*src != '\0') {
+ len = strcspn(src, trm);
+ if (len > 0) {
+ memcpy(dest, src, len);
+ dest += len;
+ src += len;
+ if (*src == '\0')
+ break;
+ }
+ *dest++ = *trm;
+ *dest++ = *trm;
+ ++src;
+ }
+
+ *dest = '\0';
+ return ret;
+}
+
+/**
+ * Encode @src into BASE-64 according to RFC 4648 and write result to @dest,
+ * which must be of appropriate size, plus one for a trailing NUL.
+ */
+static char *HX_quote_base64(char *d, const char *s)
+{
+ static const char a[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz0123456789+/";
+ size_t len = strlen(s);
+ char *ret = d;
+
+ while (len > 0) {
+ if (len >= 3) {
+ len -= 3;
+ d[0] = a[(s[0] & 0xFC) >> 2];
+ d[1] = a[((s[0] & 0x03) << 4) | ((s[1] & 0xF0) >> 4)];
+ d[2] = a[((s[1] & 0x0F) << 2) | ((s[2] & 0xC0) >> 6)];
+ d[3] = a[s[2] & 0x3F];
+ } else if (len == 2) {
+ len = 0;
+ d[0] = a[(s[0] & 0xFC) >> 2];
+ d[1] = a[((s[0] & 0x03) << 4) | ((s[1] & 0xF0) >> 4)];
+ d[2] = a[(s[1] & 0x0F) << 2];
+ d[3] = '=';
+ } else if (len == 1) {
+ len = 0;
+ d[0] = a[(s[0] & 0xFC) >> 2];
+ d[1] = a[(s[0] & 0x03) << 4];
+ d[2] = '=';
+ d[3] = '=';
+ }
+ s += 3;
+ d += 4;
+ }
+ *d = '\0';
+ return ret;
+}
+
+static size_t HX_qsize_html(const char *s)
+{
+ const char *p = s;
+ size_t n = strlen(s);
+
+ while ((p = strpbrk(p, HX_quote_rules[HXQUOTE_HTML].chars)) != NULL) {
+ switch (*p) {
+ /* minus 2: \0 and the original char */
+ case '"':
+ n += sizeof("&quot;") - 2;
+ break;
+ case '&':
+ n += sizeof("&amp;") - 2;
+ break;
+ case '<':
+ case '>':
+ n += sizeof("&lt;") - 2;
+ break;
+ }
+ ++p;
+ }
+ return n;
+}
+
+static char *HX_quote_html(char *dest, const char *src)
+{
+#define put(s) do { \
+ memcpy(dest, (s), sizeof(s) - 1); \
+ dest += sizeof(s) - 1; \
+} while (false);
+
+ char *ret = dest;
+
+ while (*src != '\0') {
+ size_t len = strcspn(src, HX_quote_rules[HXQUOTE_HTML].chars);
+ if (len > 0) {
+ memcpy(dest, src, len);
+ dest += len;
+ src += len;
+ if (*src == '\0')
+ break;
+ }
+ switch (*src++) {
+ case '"': put("&quot;"); break;
+ case '&': put("&amp;"); break;
+ case '<': put("&lt;"); break;
+ case '>': put("&gt;"); break;
+ }
+ }
+ *dest = '\0';
+ return ret;
+#undef put
+}
+
+static char *HX_quote_ldap(char *dest, const char *src, const char *qc)
+{
+ char *ret = dest;
+ size_t len;
+
+ while (*src != '\0') {
+ len = strcspn(src, qc);
+ if (len > 0) {
+ memcpy(dest, src, len);
+ dest += len;
+ src += len;
+ if (*src == '\0')
+ break;
+ }
+ *dest++ = '\\';
+ *dest++ = HX_hexenc[(*src >> 4) & 0x0F];
+ *dest++ = HX_hexenc[*src++ & 0x0F];
+ }
+
+ *dest = '\0';
+ return ret;
+}
+
+static char *HX_quote_urlenc(char *dest, const char *src)
+{
+ char *ret = dest;
+ size_t len;
+
+ while (*src != '\0') {
+ len = strspn(src, HX_quote_rules[HXQUOTE_URIENC].chars);
+ if (len > 0) {
+ memcpy(dest, src, len);
+ dest += len;
+ src += len;
+ if (*src == '\0')
+ break;
+ }
+ *dest++ = '%';
+ *dest++ = HX_hexenc[(*src >> 4) & 0x0F];
+ *dest++ = HX_hexenc[*src++ & 0x0F];
+ }
+
+ *dest = '\0';
+ return ret;
+}
+
+/**
+ * HX_quoted_size -
+ * @s: string to analyze
+ * @type: quoting method
+ *
+ * Returns the size of the string @s when quoted.
+ */
+static size_t HX_quoted_size(const char *s, unsigned int type)
+{
+ switch (type) {
+ case HXQUOTE_SQUOTE:
+ case HXQUOTE_DQUOTE:
+ case HXQUOTE_SQLSQUOTE:
+ case HXQUOTE_SQLBQUOTE:
+ return HX_qsize_bsr(s, HX_quote_rules[type].chars, 1);
+ case HXQUOTE_HTML:
+ return HX_qsize_html(s);
+ case HXQUOTE_LDAPFLT:
+ case HXQUOTE_LDAPRDN:
+ return HX_qsize_bsr(s, HX_quote_rules[type].chars, 2);
+ case HXQUOTE_BASE64:
+ return (strlen(s) + 2) / 3 * 4;
+ case HXQUOTE_URIENC:
+ return HX_qsize_bsa(s, HX_quote_rules[type].chars, 2);
+ default:
+ return strlen(s);
+ }
+}
+
+EXPORT_SYMBOL char *HX_strquote(const char *src, unsigned int type,
+ char **free_me)
+{
+ const struct HX_quote_rule *rule;
+ bool do_quote;
+ char *tmp;
+
+ if (type >= _HXQUOTE_MAX) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* If quote_chars is NULL, it is clear all chars are to be encoded. */
+ rule = &HX_quote_rules[type];
+ if (type >= ARRAY_SIZE(HX_quote_rules) || rule->chars == NULL)
+ do_quote = true;
+ else if (rule->selector == HXQUOTE_REJECT)
+ do_quote = strpbrk(src, rule->chars) != NULL;
+ else if (rule->selector == HXQUOTE_ACCEPT)
+ do_quote = HX_strchr2(src, rule->chars) != NULL;
+ else
+ do_quote = false;
+ /*
+ * free_me == NULL implies that we always allocate, even if
+ * there is nothing to quote.
+ */
+ if (free_me != NULL) {
+ free(*free_me);
+ *free_me = NULL;
+ if (!do_quote)
+ return const_cast1(char *, src);
+ } else {
+ if (!do_quote)
+ return HX_strdup(src);
+ free_me = &tmp;
+ }
+
+ *free_me = malloc(HX_quoted_size(src, type) + 1);
+ if (*free_me == NULL)
+ return NULL;
+
+ switch (type) {
+ case HXQUOTE_SQUOTE:
+ case HXQUOTE_DQUOTE:
+ return HX_quote_backslash(*free_me, src, rule->chars);
+ case HXQUOTE_HTML:
+ return HX_quote_html(*free_me, src);
+ case HXQUOTE_LDAPFLT:
+ case HXQUOTE_LDAPRDN:
+ return HX_quote_ldap(*free_me, src, rule->chars);
+ case HXQUOTE_BASE64:
+ return HX_quote_base64(*free_me, src);
+ case HXQUOTE_URIENC:
+ return HX_quote_urlenc(*free_me, src);
+ case HXQUOTE_SQLSQUOTE:
+ case HXQUOTE_SQLBQUOTE:
+ return HX_quote_sqlbackslash(*free_me, src, rule->chars);
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL char *HX_strupper(char *orig)
+{
+ char *expr;
+ for (expr = orig; *expr != '\0'; ++expr)
+ *expr = HX_toupper(*expr);
+ return orig;
+}
diff --git a/src/tc-cast.c b/src/tc-cast.c
new file mode 100644
index 0000000..73b7d66
--- /dev/null
+++ b/src/tc-cast.c
@@ -0,0 +1,95 @@
+/*
+ * Testing for compile error in the cast helpers
+ * written by Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libHX/defs.h>
+#include <libHX/init.h>
+#include "internal.h"
+#define UNUSED __attribute__((unused))
+
+static void c_signed(void)
+{
+ const char *si_00 = "foo";
+ char *si_01 = const_cast1(char *, si_00);
+ signed char *si_02 UNUSED = signed_cast(signed char *, si_01);
+ unsigned char *si_03 UNUSED = signed_cast(unsigned char *, si_01);
+ const signed char *si_04 UNUSED = signed_cast(const signed char *, si_00);
+ const unsigned char *si_05 UNUSED = signed_cast(const unsigned char *, si_00);
+ si_00 = signed_cast(const char *, si_05);
+}
+
+static void c_reinterpret(void)
+{
+ const char *si_00 = "foo";
+ void *sr_00 = reinterpret_cast(void *, static_cast(uintptr_t, 8));
+ int sr_01 = reinterpret_cast(uintptr_t, sr_00);
+ void *sr_02 = reinterpret_cast(void *, static_cast(uintptr_t, static_cast(unsigned int, reinterpret_cast(uintptr_t, &si_00))));
+ printf("sr: %p %u; %p[%p]\n", sr_00, sr_01, sr_02, &si_00);
+}
+
+static void c_static(void)
+{
+ double st_00 = sqrt(static_cast(int,
+ 10 * sqrt(static_cast(double, 3) / 4)));
+ printf("st: %f\n", st_00);
+}
+
+static void c_const1(void)
+{
+ const int *co_00 = NULL;
+ int *co_01 = const_cast1(int *, co_00);
+ co_00 = co_01;
+}
+
+static void c_const2(void)
+{
+ const int **co_02 = NULL;
+ int **co_03 UNUSED = const_cast2(int **, co_02);
+ int *const *co_04 = const_cast2(int *const *, co_02);
+ const int *const *co_05 = const_cast2(const int *const *, co_02);
+ co_02 = const_cast2(const int **, co_05);
+ co_04 = const_cast2(int *const *, co_05);
+}
+
+static void c_const3(void)
+{
+ const int *const *const *co_06 = NULL;
+ int ***co_07 UNUSED = const_cast3(int ***,
+ (printf("If this line is only printed once when the program "
+ "is run, __builtin_choose_expr works as desired.\n"), co_06));
+}
+
+static void c_constA(void)
+{
+ static const char r1[] = "static";
+ char *w1 UNUSED = const_cast1(char *, r1);
+
+ static const char *const r2[] = {"static"};
+ char **w2 UNUSED = const_cast2(char **, r2);
+
+ static const char *const *const r3[] = {NULL};
+ char ***w3 UNUSED = const_cast3(char ***, r3);
+}
+
+int main(void)
+{
+ if (HX_init() <= 0)
+ abort();
+ c_signed();
+ c_reinterpret();
+ c_static();
+ c_const1();
+ c_const2();
+ c_const3();
+ c_constA();
+ HX_exit();
+ return 0;
+}
diff --git a/src/tc-compile.c b/src/tc-compile.c
new file mode 100644
index 0000000..657f4f7
--- /dev/null
+++ b/src/tc-compile.c
@@ -0,0 +1,39 @@
+/* This file is for testing the cumulative include */
+#ifndef __cplusplus
+# include <stdlib.h>
+#else
+# include <cstdlib>
+#endif
+#include <libHX.h>
+
+#define ZZ 64
+
+int main(void)
+{
+ unsigned long long bmllong[HXbitmap_size(unsigned long long, 256)];
+ unsigned long bmlong[HXbitmap_size(unsigned long, 256)];
+ unsigned int bmint[HXbitmap_size(unsigned int, 256)];
+ unsigned short bmshort[HXbitmap_size(unsigned short, 256)];
+ unsigned char bmchar[HXbitmap_size(unsigned char, 256)];
+
+ if (HX_init() <= 0)
+ abort();
+ printf("sizeof bmllong:\t%" HX_SIZET_FMT "u, array_size: %" HX_SIZET_FMT "u\n",
+ sizeof(bmllong), ARRAY_SIZE(bmllong));
+ printf("sizeof bmlong:\t%" HX_SIZET_FMT "u, array_size: %" HX_SIZET_FMT "u\n",
+ sizeof(bmlong), ARRAY_SIZE(bmlong));
+ printf("sizeof bmint:\t%" HX_SIZET_FMT "u, array_size: %" HX_SIZET_FMT "u\n",
+ sizeof(bmint), ARRAY_SIZE(bmint));
+ printf("sizeof bmshort:\t%" HX_SIZET_FMT "u, array_size: %" HX_SIZET_FMT "u\n",
+ sizeof(bmshort), ARRAY_SIZE(bmshort));
+ printf("sizeof bmchar:\t%" HX_SIZET_FMT "u, array_size: %" HX_SIZET_FMT "u\n",
+ sizeof(bmchar), ARRAY_SIZE(bmchar));
+ HXbitmap_set(bmllong, 255);
+ HXbitmap_set(bmlong, 255);
+ HXbitmap_set(bmint, 255);
+ HXbitmap_set(bmshort, 255);
+ HXbitmap_set(bmchar, 255);
+ printf(HX_STRINGIFY(1234+2 +2) "," HX_STRINGIFY(ZZ) "\n");
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-deque.c b/src/tc-deque.c
new file mode 100644
index 0000000..e76dbc1
--- /dev/null
+++ b/src/tc-deque.c
@@ -0,0 +1,11 @@
+#ifndef __cplusplus
+# include <stdlib.h>
+#else
+# include <cstdlib>
+#endif
+#include <libHX/deque.h>
+
+int main(void)
+{
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-dir.c b/src/tc-dir.c
new file mode 100644
index 0000000..b87517a
--- /dev/null
+++ b/src/tc-dir.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#ifndef __cplusplus
+# include <stdio.h>
+# include <stdlib.h>
+#else
+# include <cstdio>
+# include <cstdlib>
+#endif
+#include <libHX/init.h>
+#include <libHX/misc.h>
+
+static void lookatdir(const char *dname)
+{
+ struct HXdir *dh;
+ const char *n;
+
+ dh = HXdir_open(dname);
+ printf("Available files in %s:\n", dname);
+ while ((n = HXdir_read(dh)) != NULL)
+ printf("\t" "%s\n", n);
+ HXdir_close(dh);
+}
+
+int main(int argc, const char **argv)
+{
+ if (HX_init() <= 0)
+ abort();
+ if (argc == 1) {
+ /* On Windows VCRT, "/" yields nothing, "c:/" is needed */
+ lookatdir("/");
+ lookatdir("c:/");
+ lookatdir(".");
+ } else {
+ while (*++argv != NULL)
+ lookatdir(*argv);
+ }
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-format.c b/src/tc-format.c
new file mode 100644
index 0000000..7e90664
--- /dev/null
+++ b/src/tc-format.c
@@ -0,0 +1,90 @@
+/*
+ * formatter test program
+ * Copyright by Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libHX/defs.h>
+#include <libHX/init.h>
+#include <libHX/option.h>
+
+static const char *const fmt2_strings[] = {
+ "HOME=%(env HOME)\n",
+ "USER=%(upper %(lower %(env USER)))\n",
+ "lower: %(lower foo, bar) %(lower %(TWOARG))\n",
+ "substr-1: %(substr Hello World,0,5) %(substr Hello World,-5,5)\n",
+ "substr-2: %(substr Hello World,-15,-6) %(substr Hello World,6)\n",
+ "substr-3: %(substr Hello World,-99) <%(substr Hello World,12,2)>\n",
+ "substr-4: <%(substr Hello World,-22,-11)> %(substr Hello World,-12,5)\n",
+ "no-exp: %%(NOEXPANSION) %NOEXPANSION\n",
+ "empty-1: <%()>\n",
+ "empty-2: <%( )>\n",
+ "empty-3: <%(dunno )>\n",
+ "empty-4: <%(echo )>\n",
+ "empty-5: <%(echo %())>\n",
+ "empty-6: <%(echo %( ))>\n",
+ "empty-7: <%(env )> <%(exec )> <%(if )> <%(if cond)> <%(lower )>\n",
+ "empty-8: <%(shell )> <%(snl )> <%(upper )>\n",
+ "basic: <%(ZERO)> <%(ONE)>\n",
+ "recursive-var: <%(%(USER))>\n",
+ "recursive-func: <%(%(env USER))>\n",
+ "ignore-escape: %(echo A\\,B) %(echo A\\)B)\n",
+ "quote-1: %(echo \"A,B\")\n",
+ "quote-2: %(echo %(echo A,B),%(echo C,D),%(echo E F))\n",
+ "quote-3: %(echo \"A)B\")\n",
+ "quote-4: %(echo foo bar) %(echo foo\\ bar)\n",
+ "unclosed-1: %(echo \"%(echo A\",B)\n",
+ "unclosed-2: %(if X,Y,Z", /* ) */
+ "nest-1: %(echo ()) %(echo %())\n",
+ "nest-2: %(echo \\(A) %(echo \\)B)\n",
+ "nest-3: %(echo \\)B\\() %(echo )B()\n",
+ "if-1: %(if %(ZERO),,\"zero is empty\")\n",
+ "if-2: %(if %(ZERO),\"zero is not empty\")\n",
+ "if-3: %(if %(ONE),,\"one is empty\")\n",
+ "if-4: %(if %(ONE),\"one is not empty\")\n",
+ "if-5: %(if %(ONE),-o%(ONE))\n",
+ "exec-1: %(exec uname -s)\n",
+ "exec-2: %(shell uname -s)\n",
+ "exec-3: %(snl %(shell uname -s))\n",
+ NULL,
+};
+
+static void t_format(int argc)
+{
+ struct HXformat_map *fmt = HXformat_init();
+ const char *const *s;
+
+ HXformat_add(fmt, "/libhx/exec", NULL, HXFORMAT_IMMED);
+ HXformat_add(fmt, "jengelh", "1337", HXTYPE_STRING | HXFORMAT_IMMED);
+ HXformat_add(fmt, "USER", "jengelh", HXTYPE_STRING | HXFORMAT_IMMED);
+ HXformat_add(fmt, "ARGC", &argc, HXTYPE_INT);
+ HXformat_add(fmt, "ARGK", reinterpret_cast(const void *, static_cast(intptr_t, argc)), HXTYPE_INT | HXFORMAT_IMMED);
+ HXformat_add(fmt, "ZERO", "", HXTYPE_STRING | HXFORMAT_IMMED);
+ HXformat_add(fmt, "ONE", "1", HXTYPE_STRING | HXFORMAT_IMMED);
+ HXformat_add(fmt, "TWOARG", "a, b", HXTYPE_STRING | HXFORMAT_IMMED);
+ ++argc;
+ printf("# HXformat2\n");
+ for (s = fmt2_strings; *s != '\0'; ++s)
+ HXformat_fprintf(fmt, stdout, *s);
+ HXformat_free(fmt);
+}
+
+int main(int argc, const char **argv)
+{
+ int ret;
+
+ ret = HX_init();
+ if (ret <= 0) {
+ fprintf(stderr, "HX_init: %s\n", strerror(-ret));
+ return EXIT_FAILURE;
+ }
+ t_format(argc);
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-link.c b/src/tc-link.c
new file mode 100644
index 0000000..e508ca9
--- /dev/null
+++ b/src/tc-link.c
@@ -0,0 +1,162 @@
+#include <stdlib.h>
+#include <libHX.h>
+#include "internal.h"
+
+static void *funcs[] = {
+ HXdeque_init,
+ HXdeque_push,
+ HXdeque_pop,
+ HXdeque_unshift,
+ HXdeque_shift,
+ HXdeque_move,
+ HXdeque_del,
+ HXdeque_free,
+ HXdeque_find,
+ HXdeque_get,
+ HXdeque_genocide2,
+ HXdeque_to_vec,
+ HX_dlopen,
+ HX_dlsym,
+ HX_dlclose,
+ HX_dlerror,
+ HXformat_init,
+ HXformat_free,
+ HXformat_add,
+ HXformat_aprintf,
+ HXformat_fprintf,
+ HXformat_sprintf,
+ HXdir_open,
+ HXdir_read,
+ HXdir_close,
+ HX_copy_file,
+ HX_copy_dir,
+ HX_mkdir,
+ HX_readlink,
+ HX_realpath,
+ HX_rrmdir,
+ HXio_fullread,
+ HXio_fullwrite,
+ HXmap_free,
+ HXhash_jlookup3,
+ HXhash_jlookup3s,
+ HXhash_djb2,
+ HXmap_init5,
+ HXmap_init,
+ HXmap_find,
+ HXmap_get,
+ HXmap_add,
+ HXmap_del,
+ HXmap_keysvalues,
+ HXmap_travinit,
+ HXmap_traverse,
+ HXmap_travfree,
+ HXmap_qfe,
+ HXmc_strinit,
+ HXmc_meminit,
+ HXmc_strcpy,
+ HXmc_memcpy,
+ HXmc_length,
+ HXmc_setlen,
+ HXmc_trunc,
+ HXmc_strcat,
+ HXmc_memcat,
+ HXmc_strpcat,
+ HXmc_mempcat,
+ HXmc_strins,
+ HXmc_memins,
+ HXmc_memdel,
+ HXmc_free,
+ HXmc_zvecfree,
+ HX_ffs,
+ HX_fls,
+ HX_hexdump,
+ HX_zvecfree,
+ HX_getopt,
+ HX_getopt_help,
+ HX_getopt_help_cb,
+ HX_getopt_usage,
+ HX_getopt_usage_cb,
+ HX_shconfig,
+ HX_shconfig_map,
+ HX_shconfig_pv,
+ HX_shconfig_free,
+ HXproc_run_async,
+ HXproc_run_sync,
+ HXproc_wait,
+ HX_init,
+ HX_exit,
+ HX_rand,
+ HX_drand,
+ HX_irand,
+ HX_basename,
+ HX_basename_exact,
+ HX_chomp,
+ HX_dirname,
+ HX_getl,
+ HX_memmem,
+ HX_split,
+ HX_split4,
+ HX_split5,
+ HX_stpltrim,
+ HX_strbchr,
+ HX_strchr2,
+ HX_strclone,
+ HX_strdup,
+ HX_strlcat,
+ HX_strlcpy,
+ HX_strlncat,
+ HX_strlower,
+ HX_strltrim,
+ HX_strmid,
+ HX_strndup,
+ HX_strnlen,
+ HX_strrcspn,
+ HX_strrev,
+ HX_strrtrim,
+ HX_strsep,
+ HX_strsep2,
+ HX_strquote,
+ HX_strupper,
+#ifdef HAVE_STRUCT_TIMEVAL_TV_USEC
+ HX_diff_timeval,
+#endif
+#ifdef HAVE_STRUCT_TIMESPEC_TV_NSEC
+ HX_diff_timespec,
+#endif
+ HX_time_compare,
+#ifdef HAVE_STRUCT_TIMESPEC_TV_NSEC
+ HX_timespec_add,
+ HX_timespec_isneg,
+ HX_timespec_mul,
+ HX_timespec_mulf,
+ HX_timespec_neg,
+ HX_timespec_sub,
+#endif
+#ifdef HAVE_STRUCT_TIMEVAL_TV_USEC
+ HX_timeval_sub,
+#endif
+#ifdef _WIN32
+ chown,
+ fchmod,
+ fchown,
+ lchown,
+ lstat,
+ mkfifo,
+ mknod,
+ readlink,
+ symlink,
+ mmap,
+ munmap,
+#endif
+};
+
+int main(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(funcs); ++i)
+ printf("%p ", funcs[i]);
+ printf("\n");
+ printf("There are %" HX_SIZET_FMT "u exported functions\n", ARRAY_SIZE(funcs));
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-list.c b/src/tc-list.c
new file mode 100644
index 0000000..c30f5aa
--- /dev/null
+++ b/src/tc-list.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#ifdef __cplusplus
+# include <cstdio>
+# include <cstdlib>
+#else
+# include <stdbool.h>
+# include <stdio.h>
+# include <stdlib.h>
+#endif
+#include <libHX/list.h>
+#include <libHX/init.h>
+#include <libHX/misc.h>
+
+struct text_object {
+ struct HXlist_head list;
+ char id[5];
+};
+
+union list_encap {
+ struct HXlist_head list;
+};
+
+static HXCLIST_HEAD(strings_ct);
+
+static void l_init(unsigned int max, bool unshift)
+{
+ static const char *const msg[] = {"Pushing", "Unshifting"};
+ struct text_object *obj;
+ unsigned int i;
+
+ for (i = 1; i <= max; ++i) {
+#ifdef __cplusplus
+ obj = new struct text_object;
+#else
+ obj = malloc(sizeof(*obj));
+#endif
+ HXlist_init(&obj->list);
+ obj->id[0] = HX_irand('a', 'z'+1);
+ obj->id[1] = HX_irand('a', 'z'+1);
+ obj->id[2] = HX_irand('a', 'z'+1);
+ obj->id[3] = HX_irand('a', 'z'+1);
+ obj->id[4] = '\0';
+ printf("%s item %u (\"%s\")\n", msg[unshift], i, obj->id);
+ if (unshift)
+ HXclist_unshift(&strings_ct, &obj->list);
+ else
+ HXclist_push(&strings_ct, &obj->list);
+ }
+}
+
+static void l_traverse(void)
+{
+ const struct text_object *obj, *safe;
+ unsigned int i;
+
+ i = 0;
+ HXlist_for_each_entry_safe(obj, safe, &strings_ct, list)
+ printf("Retrieving item %u (\"%s\")\n", ++i, obj->id);
+}
+
+static void l_dump(bool pop)
+{
+ static const char *const msg[] = {"Shifting", "Popping"};
+ struct text_object *obj;
+ unsigned int i = 0;
+
+ while ((obj = (pop ?
+ HXclist_pop(&strings_ct, struct text_object, list) :
+ HXclist_shift(&strings_ct, struct text_object, list)
+ )) != NULL)
+ printf("%s item %u (\"%s\")\n", msg[pop], ++i, obj->id);
+
+ printf("Remaining elements: %u\n", strings_ct.items);
+}
+
+static void l_empty(void)
+{
+ static const char *const fstr[] = {"fail", "pass"};
+ struct HXclist_head clh;
+ struct HXlist_head lh;
+ union list_encap *pos;
+ unsigned int count = 0;
+ bool success = true;
+
+ HXlist_init(&lh);
+ HXclist_init(&clh);
+
+ HXlist_for_each_entry(pos, &lh, list)
+ success = false;
+ HXlist_for_each_entry(pos, &clh, list)
+ success = false;
+
+ printf("Zero traversal: %s\n", fstr[success]);
+ printf("The list is indeed%s empty\n",
+ HXlist_empty(&lh) ? "" : " NOT");
+
+ HXclist_push(&clh, &lh);
+ HXlist_for_each_entry(pos, &clh, list)
+ ++count;
+
+ printf("One traversal: %s\n", fstr[count == 1]);
+ printf("The list is indeed%s empty\n",
+ HXlist_empty(&lh) ? "" : " NOT");
+}
+
+static void l_shift(void)
+{
+ /* Check for -Wshadow warnings */
+ struct object {
+ int value;
+ struct HXlist_head anchor;
+ };
+ HXCLIST_HEAD(clh);
+ struct object q, *p;
+ void *x;
+
+ q.value = 1337;
+ HXlist_init(&q.anchor);
+ HXclist_push(&clh, &q.anchor);
+ x = p = HXclist_shift(&clh, struct object, anchor);
+ printf("%d\n", p->value);
+
+ HXlist_init(&q.anchor);
+ HXclist_push(&clh, &q.anchor);
+ x = p = HXclist_pop(&clh, struct object, anchor);
+ printf("%d\n", p->value);
+}
+
+int main(int argc, const char **argv)
+{
+ unsigned int max = 10;
+
+ if (HX_init() <= 0)
+ abort();
+ if (argc >= 2)
+ max = strtoul(argv[1], NULL, 0);
+
+ l_init(max, HX_rand() & 1);
+ l_traverse();
+ l_dump(HX_rand() & 1);
+ l_empty();
+ l_shift();
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-list2.c b/src/tc-list2.c
new file mode 100644
index 0000000..f41861b
--- /dev/null
+++ b/src/tc-list2.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <libHX/list.h>
+
+struct item {
+ struct HXlist_head list;
+};
+
+int main(void)
+{
+ HXLIST_HEAD(clh);
+ struct item lh;
+ struct item *pos;
+
+ HXlist_init(&lh.list);
+ HXlist_add_tail(&clh, &lh.list);
+ HXlist_for_each_entry(pos, &clh, list)
+#ifdef OK
+ printf("%p\n", pos);
+#else
+ ;
+#endif
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-map.c b/src/tc-map.c
new file mode 100644
index 0000000..4fe0408
--- /dev/null
+++ b/src/tc-map.c
@@ -0,0 +1,747 @@
+/*
+ * Test for libHX's maps
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <libHX/init.h>
+#include <libHX/map.h>
+#include <libHX/misc.h>
+#include <libHX/string.h>
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif
+#include <sys/time.h>
+#include "internal.h"
+#include "map_int.h"
+
+union HXpoly {
+ struct HXmap *map;
+ struct HXhmap *hmap;
+ struct HXrbtree *rbt;
+};
+
+typedef struct HXmap *(*map_create4_fn_t)(unsigned int,
+ const struct HXmap_ops *, size_t, size_t);
+
+static unsigned int tmap_indent = 0;
+
+static __inline__ void tmap_ipush(void)
+{
+ ++tmap_indent;
+}
+
+static __inline__ void tmap_ipop(void)
+{
+ if (tmap_indent > 0)
+ --tmap_indent;
+}
+
+static void tmap_printf(const char *fmt, ...)
+{
+ unsigned int i;
+ va_list args;
+
+ for (i = 0; i < tmap_indent; ++i)
+ printf("\t");
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+static void tmap_time(struct timeval *tv)
+{
+#ifdef HAVE_SYS_RESOURCE_H
+ struct rusage r;
+ if (getrusage(RUSAGE_SELF, &r) == 0)
+ *tv = r.ru_utime;
+#else
+ memset(tv, 0, sizeof(*tv));
+#endif
+}
+
+static unsigned int tmap_smart_rand(unsigned int *left, unsigned int *right)
+{
+ unsigned int z = HX_irand(*left, *right);
+
+ if (z == *left)
+ ++*left;
+ else if (z == *right - 1)
+ --*right;
+ return z;
+}
+
+/**
+ * tmap_rword - create random word
+ * @dest: char buffer
+ * @length: size of buffer
+ */
+static __inline__ void tmap_rword(char *dest, unsigned int length)
+{
+ while (--length > 0)
+ *dest++ = HX_irand('a', 'z' + 1);
+ *dest = '\0';
+}
+
+static void tmap_add_rand(struct HXmap *map, unsigned int num)
+{
+ char key[8], value[HXSIZEOF_Z32];
+
+ while (num-- > 0) {
+ tmap_rword(key, sizeof(key));
+ snprintf(value, sizeof(value), "%u", num);
+ if (HXmap_add(map, key, value) == -EEXIST)
+ ++num;
+ }
+}
+
+static void tmap_flush(struct HXmap *map, bool verbose)
+{
+ const struct HXmap_node *node;
+ struct HXmap_trav *iter;
+
+ tmap_printf("Flushing %u elements (with traversal)\n", map->items);
+ tmap_ipush();
+ while (map->items != 0) {
+ /* May need to reload traverser due to deletion */
+ if (verbose)
+ tmap_printf("Restarting traverser\n");
+ if ((iter = HXmap_travinit(map, HXMAP_DTRAV)) == NULL)
+ break;
+ tmap_ipush();
+ while ((node = HXmap_traverse(iter)) != NULL) {
+ if (verbose)
+ tmap_printf("Destroying {%s, %s}\n",
+ node->skey, node->sdata);
+ HXmap_del(map, node->key);
+ }
+ tmap_ipop();
+ HXmap_travfree(iter);
+ }
+ tmap_ipop();
+}
+
+static void tmap_add_speed(struct HXmap *map)
+{
+ struct timeval start, stop, delta;
+ unsigned int threshold;
+
+ tmap_printf("MAP test 1: Timing add operation\n");
+ tmap_ipush();
+ tmap_time(&start);
+ do {
+ tmap_add_rand(map, 1);
+ tmap_time(&stop);
+ HX_timeval_sub(&delta, &stop, &start);
+ } while (!(delta.tv_sec >= 1 || map->items >= 1000000));
+ tmap_printf("%u elements in " HX_TIMEVAL_FMT
+ " (plus time measurement overhead)\n",
+ map->items, HX_TIMEVAL_EXP(&delta));
+ threshold = map->items;
+ tmap_flush(map, false);
+
+ tmap_time(&start);
+ tmap_add_rand(map, threshold);
+ tmap_time(&stop);
+ HX_timeval_sub(&delta, &stop, &start);
+ tmap_printf("%u elements in " HX_TIMEVAL_FMT " (w/o overhead)\n",
+ map->items, HX_TIMEVAL_EXP(&delta));
+ tmap_ipop();
+}
+
+static bool tmap_each_fn(const struct HXmap_node *node, void *arg)
+{
+ return true;
+}
+
+static void tmap_trav_speed(struct HXmap *map)
+{
+ struct timeval start, stop, delta, delta2;
+ const struct HXmap_node *node;
+ struct HXmap_trav *iter;
+
+ tmap_printf("MAP test 2: Timing traversal\n");
+ tmap_ipush();
+ iter = HXmap_travinit(map, HXMAP_NOFLAGS);
+ tmap_time(&start);
+ while ((node = HXmap_traverse(iter)) != NULL)
+ ;
+ tmap_time(&stop);
+ HX_timeval_sub(&delta, &stop, &start);
+ HXmap_travfree(iter);
+ tmap_printf("Open traversal of %u nodes: " HX_TIMEVAL_FMT "s\n",
+ map->items, HX_TIMEVAL_EXP(&delta));
+
+ tmap_time(&start);
+ HXmap_qfe(map, tmap_each_fn, NULL);
+ tmap_time(&stop);
+ HX_timeval_sub(&delta, &stop, &start);
+ tmap_printf("QFE traversal of %u nodes: " HX_TIMEVAL_FMT "s\n",
+ map->items, HX_TIMEVAL_EXP(&delta));
+ tmap_ipop();
+
+ tmap_printf("MAP test 2a: Timing lookup\n");
+ tmap_ipush();
+ iter = HXmap_travinit(map, HXMAP_NOFLAGS);
+ tmap_time(&start);
+ while ((node = HXmap_traverse(iter)) != NULL)
+ HXmap_find(map, node->key);
+ tmap_time(&stop);
+ HX_timeval_sub(&delta2, &stop, &start);
+ HXmap_travfree(iter);
+ /* delta2 includes traversal time */
+ start = delta;
+ stop = delta2;
+ HX_timeval_sub(&delta, &stop, &start);
+ tmap_printf("Lookup of %u nodes: " HX_TIMEVAL_FMT "s\n",
+ map->items, HX_TIMEVAL_EXP(&delta));
+ tmap_ipop();
+}
+
+static void tmap_flat(const struct HXmap *map)
+{
+ struct HXmap_node *nodes;
+ unsigned int i;
+
+ tmap_printf("Retrieving flattened list of %u elements:\n", map->items);
+ tmap_ipush();
+ nodes = HXmap_keysvalues(map);
+ if (nodes == NULL) {
+ perror("HXmap_keysvalues");
+ abort();
+ }
+ for (i = 0; i < map->items; ++i)
+ tmap_printf("%u. %s -> %s\n", i, nodes[i].key, nodes[i].data);
+ tmap_ipop();
+ free(nodes);
+}
+
+static void tmap_trav(struct HXmap *map)
+{
+ const struct HXmap_node *node;
+ unsigned int i = ~(~0U >> 1);
+ char key[8], value[HXSIZEOF_Z32];
+ struct HXmap_trav *iter;
+
+ tmap_printf("Simple traversal:\n");
+ tmap_ipush();
+ iter = HXmap_travinit(map, HXMAP_NOFLAGS);
+ while ((node = HXmap_traverse(iter)) != NULL)
+ tmap_printf("%s -> %s\n", node->skey, node->sdata);
+ tmap_ipop();
+ HXmap_travfree(iter);
+
+ tmap_printf("Add modification during traversal:\n");
+ tmap_ipush();
+ iter = HXmap_travinit(map, HXMAP_NOFLAGS);
+ while ((node = HXmap_traverse(iter)) != NULL) {
+ tmap_printf("%s -> %s\n", node->skey, node->sdata);
+ tmap_rword(key, sizeof(key));
+ snprintf(value, sizeof(value), "%u", i++);
+ HXmap_add(map, key, value);
+ }
+ tmap_ipop();
+ HXmap_travfree(iter);
+}
+
+static void tmap_generic_tests(enum HXmap_type type,
+ unsigned long (*hash_fn)(const void *, size_t), const char *hash_name)
+{
+ struct HXmap_ops ops = {.k_hash = hash_fn};
+ struct HXmap *map;
+
+ tmap_printf("Using hash %s\n", hash_name);
+ map = HXmap_init5(type, HXMAP_SCKEY | HXMAP_SCDATA | HXMAP_NOREPLACE,
+ &ops, 0, 0);
+ tmap_add_speed(map);
+ tmap_trav_speed(map);
+ tmap_flush(map, false);
+
+ tmap_add_rand(map, 2);
+ tmap_flat(map);
+ tmap_trav(map);
+ tmap_flush(map, true);
+ HXmap_free(map);
+}
+
+static int tmap_strtolcmp(const void *a, const void *b, size_t z)
+{
+ long p = strtol(static_cast(const char *, a), NULL, 0);
+ long q = strtol(static_cast(const char *, b), NULL, 0);
+
+ if (p < q)
+ return -1;
+ if (p > q)
+ return 1;
+ return 0;
+}
+
+static const struct HXmap_ops tmap_nstr_ops = {
+ .k_compare = tmap_strtolcmp,
+ .k_hash = HXhash_djb2,
+};
+
+static const struct HXmap_ops tmap_nstr_l3_ops = {
+ .k_compare = tmap_strtolcmp,
+ .k_hash = HXhash_jlookup3s,
+};
+
+static const struct HXmap_ops tmap_words_ops = {
+ .k_hash = HXhash_djb2,
+};
+
+static const struct HXmap_ops tmap_words_l3_ops = {
+ .k_hash = HXhash_jlookup3s,
+};
+
+/**
+ * tmap_expect - compare two strings or warn
+ * @result: result from previous operations
+ * @expected: what we think should have happened
+ */
+static int tmap_expect(const char *result, const char *expected)
+{
+ int ret = strcmp(result, expected);
+ tmap_ipush();
+ tmap_printf("Expected: %s\n", expected);
+ tmap_printf(" Result: %s\n", result);
+ if (ret != 0) {
+ tmap_ipush();
+ tmap_printf("...failed\n");
+ tmap_ipop();
+ }
+ tmap_ipop();
+ return ret;
+}
+
+/**
+ * tmap_new_perfect_tree -
+ * Add elements in such a way that it does not cause an rbtree to rebalance and
+ * thus deterministically attain a perfect binary tree. For hash maps, it only
+ * serves to add some elements.
+ */
+static void tmap_new_perfect_tree(struct HXmap *map,
+ unsigned int height, unsigned int mult)
+{
+ unsigned int right = 1 << height;
+ unsigned int incr = right;
+ unsigned int left = incr / 2;
+ unsigned int y, x;
+ char buf[HXSIZEOF_Z32];
+
+ for (y = 0; y < height; ++y) {
+ for (x = left; x < right; x += incr) {
+ snprintf(buf, sizeof(buf), "%u", x * mult);
+ HXmap_add(map, buf, NULL);
+ }
+ incr /= 2;
+ left /= 2;
+ }
+}
+
+/**
+ * Compute an "agglomeration" index that models the lack of distributedness
+ * in hash maps. Range is 0-100%.
+ */
+static double hmap_agg_index(const struct HXhmap *hmap, bool verbose)
+{
+ const struct HXhmap_node *hnode;
+ unsigned int i;
+ int f = 0, j;
+
+ if (hmap->super.items == 1)
+ return 0;
+ if (verbose)
+ printf("{");
+
+ /*
+ * HXhmap is written such that the number of buckets is always equal or
+ * greater than the element count. This is done because, in practice,
+ * buckets will be populated with more than a few (two/three) entries
+ * before elements/buckets >= grow_trigger_ratio.
+ *
+ * Therefore, one could distribute elements such that no bucket
+ * contains more than one. This is the "ideal" situation. We now count
+ * the sum of absolute differences from this ideal, abs(1-j).
+ *
+ */
+ for (i = 0; i < HXhash_primes[hmap->power]; ++i) {
+ j = 0;
+ HXlist_for_each_entry(hnode, &hmap->bk_array[i], anchor)
+ ++j;
+ if (verbose)
+ printf("%u,", j);
+ /*
+ * The --j thing looks a little odd on review, but actually
+ * just does j=abs(1-j), but unlike abs, can handle a range
+ * nearly as large as unsigned int, were it to use something
+ * like j==(unsigned int)-1 instead of j<0.
+ *
+ * j=0 => j=-1 => j=+1
+ * j=1 => j= 0 => j= 0
+ * j=2 => j=+1 => j=+1
+ */
+ --j;
+ if (j < 0)
+ j = -j;
+ f += j;
+ }
+ if (verbose)
+ printf("}\n");
+ /* Ignore buckets that must logically be empty (pigeonhole principle) */
+ f -= HXhash_primes[hmap->power] - hmap->super.items;
+ /*
+ * Since we counted both underpopulation (0 elements in a bucket) as
+ * well as overpopulation (more than 1 element in a bucket), @f needs
+ * to be divided by two, making it f/(2*(e-1)).
+ */
+ /* Now return % */
+ return static_cast(double, 50 * f) / (hmap->super.items - 1);
+}
+
+/**
+ * Test one hash function with different keys and check agglomeration index.
+ */
+static void tmap_hmap_test_1a(const char *map_type,
+ unsigned long (*hash_fn)(const void *, size_t), unsigned int max_power)
+{
+ struct HXmap_ops intstr_ops = {
+ .k_compare = tmap_strtolcmp,
+ .k_hash = hash_fn,
+ };
+ struct HXmap_ops words_ops = {
+ .k_hash = hash_fn,
+ };
+ unsigned int power;
+ union HXpoly u;
+
+ for (power = 1; power <= max_power; ++power) {
+ u.map = HXmap_init5(HXMAPT_HASH, HXMAP_SCKEY,
+ &intstr_ops, 0, 0);
+ tmap_new_perfect_tree(u.map, power, 2);
+ tmap_printf("%s, intstr, %u items/%u buckets, "
+ "agglomeration: %.2f%%\n", map_type,
+ u.map->items, HXhash_primes[u.hmap->power],
+ hmap_agg_index(u.hmap, false));
+ HXmap_free(u.map);
+ }
+
+ u.map = HXmap_init5(HXMAPT_HASH, HXMAP_SCKEY, &words_ops, 0, 0);
+ while (u.map->items < 1 << max_power) {
+ /* Fill up just right up to the maximum load */
+ tmap_add_rand(u.map, u.hmap->max_load - u.map->items);
+ tmap_printf("%s, words, %u items/%u buckets, "
+ "agglomeration: %.2f%%\n", map_type,
+ u.map->items, HXhash_primes[u.hmap->power],
+ hmap_agg_index(u.hmap, false));
+ /* trigger resize */
+ tmap_add_rand(u.map, 1);
+ tmap_printf("%s, words, %u items/%u buckets, "
+ "agglomeration: %.2f%%\n", map_type,
+ u.map->items, HXhash_primes[u.hmap->power],
+ hmap_agg_index(u.hmap, false));
+ }
+ HXmap_free(u.map);
+}
+
+/**
+ * tmap_hmap_test_1 - test distributedness of elements
+ */
+static void tmap_hmap_test_1(void)
+{
+ static const unsigned int max_power = 15;
+
+ tmap_printf("HMAP test 1A: Hashmap distribution\n");
+ tmap_ipush();
+ tmap_hmap_test_1a("DJB2", HXhash_djb2, max_power);
+ tmap_hmap_test_1a("JL3", HXhash_jlookup3s, max_power);
+ tmap_ipop();
+}
+
+static void __rbt_walk_tree(const struct HXrbtree_node *node,
+ char *buf, size_t s)
+{
+ bool has_children = node->sub[0] != NULL || node->sub[1] != NULL;
+ HX_strlcat(buf, node->skey, s);
+
+ if (node->color == RBT_BLACK)
+ HX_strlcat(buf, "%b", s);
+ if (has_children)
+ HX_strlcat(buf, "(" /* ) */, s);
+ if (node->sub[0] != NULL)
+ __rbt_walk_tree(node->sub[0], buf, s);
+ if (node->sub[1] != NULL) {
+ HX_strlcat(buf, ",", s);
+ __rbt_walk_tree(node->sub[1], buf, s);
+ }
+ if (has_children)
+ HX_strlcat(buf, /* ( */ ")", s);
+}
+
+/**
+ * rbt_walk_tree - walk the tree and provide a string suitable for texitree
+ * @node: node of an rbtree to start diving at
+ * @buf: buffer for texitree representation
+ * @size: size for @buf
+ */
+static void rbt_walk_tree(const struct HXrbtree_node *node,
+ char *buf, size_t size)
+{
+ *buf = '\0';
+ __rbt_walk_tree(node, buf, size);
+}
+
+/**
+ * rbt_new_perfect_tree - generate a perfect binary tree
+ * @height: height of the desired tree
+ * @mult: multiplicator for node numbers
+ *
+ * Produces a tree of desired height with exactly 2^height-1 nodes.
+ */
+static struct HXmap *rbt_new_perfect_tree(unsigned int height,
+ unsigned int mult)
+{
+ struct HXmap *tree =
+ HXmap_init5(HXMAPT_RBTREE, HXMAP_SCKEY, &tmap_nstr_ops, 0, 0);
+ tmap_new_perfect_tree(tree, height, mult);
+ return tree;
+}
+
+static unsigned int rbt_tree_height(const struct HXrbtree_node *node)
+{
+ unsigned int a = 1, b = 1;
+ if (node->sub[0] != NULL)
+ a += rbt_tree_height(node->sub[0]);
+ if (node->sub[1] != NULL)
+ b += rbt_tree_height(node->sub[1]);
+ return (a > b) ? a : b;
+}
+
+static void rbt_height_check(const struct HXrbtree *tree)
+{
+ double min, max, avg;
+ min = log(tree->super.items + 1) / log(2);
+ max = 2 * log(tree->super.items + 1) / log(2);
+ avg = log((pow(2, min) + pow(2, max)) / 2) / log(2);
+ tmap_ipush();
+ tmap_printf("%u items; height %u; min/avg/max %.2f/%.2f/%.2f\n",
+ tree->super.items, rbt_tree_height(tree->root),
+ min, avg, max);
+ tmap_ipop();
+}
+
+/**
+ * tmap_rbt_test_1 - basic rbt node layout tests
+ */
+static void tmap_rbt_test_1(void)
+{
+ union HXpoly u;
+ char buf[80];
+
+ tmap_printf("RBT test 1A: Creating tree with 7 nodes (height 3)\n");
+ u.map = rbt_new_perfect_tree(3, 2);
+
+ tmap_printf("RBT test 1B: Manual traverse\n");
+ rbt_walk_tree(u.rbt->root, buf, sizeof(buf));
+
+ tmap_printf("RBT test 1C: Check for correct positions and colors\n");
+ tmap_expect(buf, "8%b(4%b(2,6),12%b(10,14))");
+
+ /* 8
+ * / \
+ * 4 12
+ * / \ / \
+ * 2 6 10 14
+ * /
+ * 9
+ */
+ tmap_printf("RBT test 1D: Node insertion and test for positions/colors\n");
+ HXmap_add(u.map, "9", NULL);
+ rbt_walk_tree(u.rbt->root, buf, sizeof(buf));
+ tmap_expect(buf, "8%b(4%b(2,6),12(10%b(9),14%b))");
+
+ tmap_printf("RBT test 1E: Height check\n");
+ rbt_height_check(u.rbt);
+
+ tmap_printf("RBT test 1G: Node deletion\n");
+ HXmap_del(u.map, "8");
+ rbt_walk_tree(u.rbt->root, buf, sizeof(buf));
+ tmap_expect(buf, "9%b(4%b(2,6),12(10%b,14%b))");
+
+ /* 9 (8 replaced by its in-order successor 9)
+ * / \
+ * 4 12
+ * / \ / \
+ * 2 6 10 14
+ */
+ HXmap_free(u.map);
+}
+
+/**
+ * rbt_no_2red_children - verify rbtree rule
+ * @node: subtree to verify
+ *
+ * Verify that there are no red nodes with red children.
+ */
+static bool rbt_no_2red_children(const struct HXrbtree_node *node)
+{
+ if (node->sub[RBT_LEFT] != NULL) {
+ if (node->color == RBT_RED &&
+ node->sub[RBT_LEFT]->color == RBT_RED)
+ return false;
+ if (!rbt_no_2red_children(node->sub[RBT_LEFT]))
+ return false;
+ }
+ if (node->sub[RBT_RIGHT] != NULL) {
+ if (node->color == RBT_RED &&
+ node->sub[RBT_RIGHT]->color == RBT_RED)
+ return false;
+ if (!rbt_no_2red_children(node->sub[RBT_RIGHT]))
+ return false;
+ }
+ return true;
+}
+
+/**
+ * rbt_black_height - calculate the black height of a tree
+ * @node: subtree to find the black height for
+ *
+ * Returns the black height, or -1 if the black height is not consistent.
+ */
+static int rbt_black_height(const struct HXrbtree_node *node)
+{
+ int lh = 0, rh = 0;
+
+ if (node->sub[RBT_LEFT] != NULL)
+ if ((lh = rbt_black_height(node->sub[RBT_LEFT])) == -1)
+ return -1;
+ if (node->sub[RBT_RIGHT] != NULL)
+ if ((rh = rbt_black_height(node->sub[RBT_RIGHT])) == -1)
+ return -1;
+ if (node->sub[RBT_LEFT] != NULL && node->sub[RBT_RIGHT] != NULL)
+ if (lh != rh)
+ return -1;
+ if (node->sub[RBT_LEFT] != NULL)
+ return lh + (node->color == RBT_BLACK);
+ else
+ return rh + (node->color == RBT_BLACK);
+}
+
+static bool rbt_verify_tree(const struct HXrbtree_node *root)
+{
+ /* Root is black */
+ if (root->color != RBT_BLACK) {
+ tmap_printf("Root is not black\n");
+ return false;
+ }
+ /* A red node may not have any red children */
+ if (!rbt_no_2red_children(root)) {
+ tmap_printf("Red node may not have red children violated\n");
+ return false;
+ }
+ /* Black height must be consistent */
+ if (rbt_black_height(root) < 0) {
+ tmap_printf("Black height violated\n");
+ return false;
+ }
+ return true;
+}
+
+/**
+ * rbt_peel_tree - slowly destroy tree and check characteristics
+ * @tree: the object to disseminate
+ * @range: original number of elements in the tree
+ */
+static void rbt_peel_tree(union HXpoly u, unsigned int range)
+{
+ unsigned int left = 1;
+
+ while (u.map->items != 0) {
+ uintptr_t number = tmap_smart_rand(&left, &range);
+
+ HXmap_del(u.map, reinterpret_cast(const void *, number));
+ if (errno == -ENOENT)
+ continue;
+ if (u.map->items == 0)
+ break;
+ if (!rbt_verify_tree(u.rbt->root))
+ return;
+ }
+}
+
+static void tmap_rbt_test_7(void)
+{
+ unsigned int i, elems, order, left, right;
+ union HXpoly u;
+ int ret;
+
+ tmap_printf("RBT test 7: AMOV/DMOV\n");
+ tmap_ipush();
+ u.map = HXmap_init(HXMAPT_RBTREE, 0);
+ for (order = 2; order <= 10; ++order) {
+ elems = (1 << order) - 1;
+ tmap_printf("Tree of order %u [e=%u]\n", order, elems);
+
+ /* Build a random tree */
+ left = 1;
+ right = elems + 1;
+ for (i = 0; i < elems; ++i) {
+ uintptr_t z = tmap_smart_rand(&left, &right);
+
+ ret = HXmap_add(u.map,
+ reinterpret_cast(const void *, z), NULL);
+ if (ret == -EEXIST)
+ --i;
+ if (!rbt_verify_tree(u.rbt->root))
+ tmap_printf("Verification failed\n");
+ }
+ /* Dismantle. */
+ rbt_height_check(u.rbt);
+ rbt_peel_tree(u, elems + 1);
+ }
+ tmap_ipop();
+ HXmap_free(u.map);
+}
+
+static void tmap_zero(void)
+{
+ struct HXmap *b;
+
+ b = HXmap_init(HXMAPT_DEFAULT, HXMAP_CKEY | HXMAP_CDATA);
+ if (b != NULL)
+ fprintf(stderr, "eek!\n");
+ b = HXmap_init(HXMAPT_DEFAULT, HXMAP_CKEY);
+ if (b != NULL)
+ fprintf(stderr, "eek!\n");
+}
+
+int main(void)
+{
+ if (HX_init() <= 0)
+ abort();
+
+ tmap_zero();
+
+ tmap_printf("* HXhashmap\n");
+ tmap_generic_tests(HXMAPT_HASH, HXhash_djb2, "DJB2");
+ tmap_generic_tests(HXMAPT_HASH, HXhash_jlookup3s, "JL3");
+ tmap_hmap_test_1();
+
+ tmap_printf("\n* RBtree\n");
+ tmap_generic_tests(HXMAPT_RBTREE, NULL, "<NONE>");
+ tmap_rbt_test_1();
+ tmap_rbt_test_7();
+
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-memmem.c b/src/tc-memmem.c
new file mode 100644
index 0000000..9c59c7f
--- /dev/null
+++ b/src/tc-memmem.c
@@ -0,0 +1,73 @@
+/*
+ * speed test HX_memmem
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <libHX/init.h>
+#include <libHX/misc.h>
+#include <libHX/string.h>
+
+static unsigned int size = 1048576 * 64;
+static const char filler_text[] =
+ "Slhrdlu cringle tongle flonging blobbity bleep blingmangl";
+
+static void long_scan(void)
+{
+ struct timespec start, stop, delta;
+ char *filler2, *p;
+
+ filler2 = malloc(size);
+ if (filler2 == NULL)
+ abort();
+ memset(filler2, 'l', size);
+ filler2[size-2] = 'a';
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
+ p = HX_memmem(filler2, strlen(filler2), "al", 2);
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &stop);
+ printf("long_scan: filler2=%p p=%p\n", filler2, p);
+ HX_timespec_sub(&delta, &stop, &start);
+ printf("long_scan: " HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
+}
+
+int main(void)
+{
+ unsigned int i;
+ char *haystack;
+ struct timespec start, stop, delta;
+
+ if (HX_init() <= 0)
+ abort();
+ haystack = malloc(size);
+ if (haystack == NULL)
+ abort();
+ memset(haystack, 'A', size);
+ haystack[size-1] = haystack[size-2] = 'Z';
+ printf("Init done\n");
+ printf("Start=%p End=%p\n", filler_text,
+ filler_text + ARRAY_SIZE(filler_text));
+ printf("%p\n", HX_memmem(filler_text, strlen(filler_text), "nangl", 5));
+ printf("%p\n", HX_memmem(filler_text, strlen(filler_text), "angl", 4));
+ printf("%p\n", HX_memmem(filler_text, strlen(filler_text), "ngl", 3));
+ printf("%p\n", HX_memmem(filler_text, strlen(filler_text), "ngl", 3));
+
+ long_scan();
+
+ for (i = 0; i < 10; ++i) {
+ printf("Search length %u...", i);
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
+ HX_memmem(haystack, size, haystack + size - i, i);
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &stop);
+ HX_timespec_sub(&delta, &stop, &start);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
+ }
+
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-misc.c b/src/tc-misc.c
new file mode 100644
index 0000000..cb985a3
--- /dev/null
+++ b/src/tc-misc.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#ifndef __cplusplus
+# include <stdlib.h>
+#else
+# include <cstdlib>
+#endif
+#include <sys/stat.h>
+#include <libHX/init.h>
+#include <libHX/misc.h>
+
+int main(int argc, const char **argv)
+{
+ unsigned int n;
+ struct stat sa, sb;
+
+ if (HX_init() <= 0)
+ abort();
+ printf("%d\n", HX_ffs(0));
+ for (n = 1; ; n <<= 1) {
+ printf("%08x = %d\n", n, HX_ffs(n));
+ if (n & 0x80000000)
+ break;
+ }
+ printf("---\n");
+ for (n = 1; ; n <<= 1, n |= 1) {
+ printf("%08x = %d\n", n, HX_ffs(n));
+ if (n == ~0U)
+ break;
+ }
+
+ if (argc >= 3) {
+ if (stat(argv[1], &sa) < 0 ||
+ stat(argv[2], &sb) < 0)
+ perror("stat");
+ printf("Difference: %ld\n", HX_time_compare(&sa, &sb, 'm'));
+ }
+
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-netio.c b/src/tc-netio.c
new file mode 100644
index 0000000..45a6efc
--- /dev/null
+++ b/src/tc-netio.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#define WIN32_LEAN_AND_MEAN 1
+#ifdef __cplusplus
+# include <cstdlib>
+# include <cstdio>
+# include <cstring>
+#else
+# include <stdlib.h>
+# include <stdio.h>
+# include <string.h>
+#endif
+#include <unistd.h>
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <sys/socket.h>
+# include <netdb.h>
+#endif
+#include <libHX/init.h>
+#include <libHX/io.h>
+
+int main(void)
+{
+ const char id[] = "SSH-2.0-OpenSSH_9.9";
+ struct addrinfo *res;
+ int fd, ret;
+
+ if ((ret = HX_init()) <= 0) {
+ fprintf(stderr, "HX_init: %s\n", strerror(-ret));
+ abort();
+ }
+
+ fd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ abort();
+ }
+ if (getaddrinfo("::1", "22", NULL, &res) < 0) {
+ perror("getaddrinfo");
+ abort();
+ }
+ if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
+ perror("connect");
+ abort();
+ }
+ if (HXio_fullwrite(fd, id, strlen(id)) < 0)
+ perror("write");
+ close(fd);
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-option.c b/src/tc-option.c
new file mode 100644
index 0000000..dbd595c
--- /dev/null
+++ b/src/tc-option.c
@@ -0,0 +1,119 @@
+/*
+ * option parser test program
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libHX/defs.h>
+#include <libHX/init.h>
+#include <libHX/map.h>
+#include <libHX/option.h>
+
+static int opt_v = 0, opt_mask = 0;
+static char *opt_kstr = NULL;
+static long opt_klong = 0;
+static double opt_kdbl = 0;
+static int opt_kflag = 0, opt_kint = 0;
+static int opt_dst = 0;
+static hxmc_t *opt_mcstr = NULL;
+
+static void opt_cbf(const struct HXoptcb *cbi)
+{
+ printf("cbf was called... with \"%s\"/'%c'\n",
+ cbi->current->ln, cbi->current->sh);
+}
+
+static const char *opt_eitheror[] = {"neither", "either", "or"};
+static struct HXoption table[] = {
+ {.ln = "dbl", .type = HXTYPE_DOUBLE, .cb = opt_cbf,
+ .ptr = &opt_kdbl, .help = "Callback function for doubles"},
+ {.ln = "flag", .sh = 'F', .type = HXTYPE_NONE, .cb = opt_cbf,
+ .ptr = &opt_kflag, .help = "Callback function for flags"},
+ {.ln = "long", .sh = 'L', .type = HXTYPE_LONG, .cb = opt_cbf,
+ .ptr = &opt_klong, .help = "Callback function for integers"},
+ {.sh = 'B', .type = HXTYPE_BOOL, .ptr = &opt_v,
+ .cb = opt_cbf, .help = "Bool test", .htyp = "value"},
+ {.sh = 'P', .type = HXTYPE_MCSTR, .ptr = &opt_mcstr,
+ .help = "Any string"},
+ {.ln = "str", .sh = 'S', .type = HXTYPE_STRING, .cb = opt_cbf,
+ .ptr = &opt_kstr, .help = "Callback function for strings"},
+ {.ln = "either", .type = HXTYPE_VAL, .cb = opt_cbf, .ptr = &opt_dst,
+ .val = 1, .help = "Mutually exclusive selection: either | or"},
+ {.ln = "or", .type = HXTYPE_VAL, .ptr = &opt_dst, .val = 2,
+ .cb = opt_cbf, .help = "Mutually exclusive selection: either | or"},
+ {.ln = "quiet", .sh = 'q', .type = HXOPT_DEC, .ptr = &opt_v,
+ .cb = opt_cbf, .help = "Decrease verbosity"},
+ {.ln = "verbose", .sh = 'v', .type = HXOPT_INC, .ptr = &opt_v,
+ .cb = opt_cbf, .help = "Increase verbosity"},
+ {.sh = 'A', .type = HXTYPE_INT | HXOPT_AND, .ptr = &opt_mask,
+ .cb = opt_cbf, .help = "AND mask test", .htyp = "value"},
+ {.sh = 'O', .type = HXTYPE_INT | HXOPT_OR, .ptr = &opt_mask,
+ .cb = opt_cbf, .help = "OR mask test", .htyp = "value"},
+ {.sh = 'X', .type = HXTYPE_INT | HXOPT_XOR, .ptr = &opt_mask,
+ .cb = opt_cbf, .help = "XOR mask test", .htyp = "value"},
+ {.sh = 'G', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf},
+ {.sh = 'H', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf},
+ {.sh = 'I', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf},
+ HXOPT_AUTOHELP,
+ {.sh = 'J', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf},
+ HXOPT_TABLEEND,
+};
+
+static void dump_argv(const char **v)
+{
+ while (*v != NULL)
+ printf("[%s] ", *v++);
+ printf("\n");
+}
+
+static void t_pthru(void)
+{
+ const char *argv[] = {
+ "ARGV0", "-Zomg", "-GZfoo", "bar",
+ "--unknown-f=13.37", "--unknown-a",
+ "foo", "bar", NULL
+ };
+ const char **argp = argv;
+ int argc = ARRAY_SIZE(argv) - 1;
+
+ printf("PTHRU test:\n");
+ HX_getopt(table, &argc, &argp, HXOPT_USAGEONERR | HXOPT_PTHRU);
+ dump_argv(argp);
+ printf("\n");
+}
+
+static void t_empty_argv(void)
+{
+ const char *zero_argv[] = {NULL}, **zero_argp = zero_argv;
+ int zero_argc = 0;
+
+ printf("Testing argv={NULL}\n");
+ HX_getopt(table, &zero_argc, &zero_argp, HXOPT_USAGEONERR);
+}
+
+int main(int argc, const char **argv)
+{
+ if (HX_init() <= 0)
+ abort();
+ printf("Return value of HX_getopt: %d\n",
+ HX_getopt(table, &argc, &argv, HXOPT_USAGEONERR));
+ t_empty_argv();
+
+ printf("Either-or is: %s\n", opt_eitheror[opt_dst]);
+ printf("values: D=%lf I=%d L=%ld S=%s\n",
+ opt_kdbl, opt_kint, opt_klong, opt_kstr);
+ printf("Verbosity level: %d\n", opt_v);
+ printf("Mask: 0x%08X\n", opt_mask);
+ printf("mcstr: >%s<\n", opt_mcstr);
+
+ t_pthru();
+
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-proc.c b/src/tc-proc.c
new file mode 100644
index 0000000..7ec982f
--- /dev/null
+++ b/src/tc-proc.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#ifdef __cplusplus
+# include <cerrno>
+# include <cstdlib>
+# include <cstring>
+#else
+# include <errno.h>
+# include <stdlib.h>
+# include <string.h>
+#endif
+#include <unistd.h>
+#include <libHX/init.h>
+#include <libHX/proc.h>
+#include <libHX/string.h>
+
+static const char *const t_args1[] = {"ls", "ls", "-dl", ".", NULL};
+static const char *const t_args2[] = {"ls", "ls", "-l", NULL};
+static const char *const t_args3[] = {"ls", "-l", "/proc/self/fd/", NULL};
+
+static void t_async1(void)
+{
+ FILE *fp;
+ int ret;
+ unsigned int i = 0;
+ hxmc_t *line = NULL;
+ struct HXproc proc;
+
+ memset(&proc, 0, sizeof(proc));
+ proc.p_flags = HXPROC_A0 | HXPROC_STDOUT;
+
+ if ((ret = HXproc_run_async(t_args2, &proc)) <= 0) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return;
+ }
+ if ((fp = fdopen(proc.p_stdout, "r")) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ goto out;
+ }
+ while (HX_getl(&line, fp) != NULL)
+ printf("\t#%u\t%s", ++i, line);
+
+ fclose(fp);
+ out:
+ close(proc.p_stdout);
+ HXproc_wait(&proc);
+ return;
+}
+
+int main(void)
+{
+ if (HX_init() <= 0)
+ abort();
+
+ /* let it fail - test verbosity */
+ HXproc_run_sync(t_args1 + 2, HXPROC_VERBOSE);
+ HXproc_run_sync(t_args1 + 3, HXPROC_VERBOSE);
+
+ HXproc_run_sync(t_args1 + 1, 0);
+
+ t_async1();
+ HXproc_run_sync(t_args3, HXPROC_NULL_STDIN);
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-rand.c b/src/tc-rand.c
new file mode 100644
index 0000000..3af45d0
--- /dev/null
+++ b/src/tc-rand.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <libHX/init.h>
+#include <libHX/misc.h>
+#include "internal.h"
+
+int main(void)
+{
+ struct timespec past, now, delta;
+ unsigned int i;
+
+ if (HX_init() <= 0)
+ abort();
+ for (i = 0; i < 15; ++i) {
+ printf("%d ", HX_irand(i, i));
+ printf("%.1f ", HX_drand(i, i));
+ printf("%08x, ", HX_irand(0, RAND_MAX));
+ }
+ printf("\n");
+
+ clock_gettime(CLOCK_REALTIME, &past);
+ for (i = 0; i < (1 << 25); ++i) {
+ volatile unsigned int __attribute__((unused)) t =
+ HX_irand(0, RAND_MAX);
+ }
+ clock_gettime(CLOCK_REALTIME, &now);
+ HX_timespec_sub(&delta, &now, &past);
+ printf("%% method: " HX_TIMESPEC_FMT " s\n", HX_TIMESPEC_EXP(&delta));
+
+ clock_gettime(CLOCK_REALTIME, &past);
+ for (i = 0; i < (1 << 25); ++i) {
+ volatile unsigned int __attribute__((unused)) t =
+ HX_irand(0, ~0U);
+ }
+ clock_gettime(CLOCK_REALTIME, &now);
+ HX_timespec_sub(&delta, &now, &past);
+ printf("/ method: " HX_TIMESPEC_FMT " s\n", HX_TIMESPEC_EXP(&delta));
+
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-realpath.c b/src/tc-realpath.c
new file mode 100644
index 0000000..5dd9aa2
--- /dev/null
+++ b/src/tc-realpath.c
@@ -0,0 +1,64 @@
+/*
+ * Test utility for libHX's realpath
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libHX/io.h>
+#include <libHX/option.h>
+
+static unsigned int rp_flags;
+static unsigned int rp_absolute;
+static unsigned int rp_no_parent, rp_no_self;
+
+static const struct HXoption rp_option_table[] = {
+ {.sh = 'a', .type = HXTYPE_NONE, .ptr = &rp_absolute,
+ .help = "Produce an absolute path"},
+ {.sh = 'p', .type = HXTYPE_NONE, .ptr = &rp_no_parent,
+ .help = "Deactivate resolution of \"..\" entries"},
+ {.sh = 's', .type = HXTYPE_NONE, .ptr = &rp_no_self,
+ .help = "Deactivate resolution of \".\" entries"},
+ HXOPT_AUTOHELP,
+ HXOPT_TABLEEND,
+};
+
+static bool rp_get_options(int *argc, const char ***argv)
+{
+ if (HX_getopt(rp_option_table, argc, argv, HXOPT_USAGEONERR) !=
+ HXOPT_ERR_SUCCESS)
+ return false;
+ rp_flags = HX_REALPATH_DEFAULT;
+ if (rp_absolute)
+ rp_flags |= HX_REALPATH_ABSOLUTE;
+ if (rp_no_parent)
+ rp_flags &= ~HX_REALPATH_PARENT;
+ if (rp_no_self)
+ rp_flags &= ~HX_REALPATH_SELF;
+ return true;
+}
+
+int main(int argc, const char **argv)
+{
+ hxmc_t *res;
+ int ret;
+
+ if (!rp_get_options(&argc, &argv))
+ return EXIT_FAILURE;
+
+ res = NULL;
+ while (--argc > 0) {
+ ret = HX_realpath(&res, *++argv, rp_flags);
+ if (ret < 0) {
+ perror("HX_realpath");
+ printf("\n");
+ } else {
+ printf("%s\n", res);
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-shconfig.c b/src/tc-shconfig.c
new file mode 100644
index 0000000..8a31c45
--- /dev/null
+++ b/src/tc-shconfig.c
@@ -0,0 +1,63 @@
+/*
+A=b;C="d" ; E="F;" ; F= G=Z
+*/
+/*
+ * shconfig test program
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libHX/init.h>
+#include <libHX/map.h>
+#include <libHX/option.h>
+
+static void t_shconfig(const char *file)
+{
+ char *A, *C, *E;
+ struct HXoption opt_tab[] = {
+ {.ln = "A", .type = HXTYPE_STRING, .ptr = &A},
+ {.ln = "C", .type = HXTYPE_STRING, .ptr = &C},
+ {.ln = "E", .type = HXTYPE_STRING, .ptr = &E},
+ HXOPT_TABLEEND,
+ };
+ if (HX_shconfig(file, opt_tab) < 0)
+ fprintf(stderr, "Read error %s: %s\n", file, strerror(errno));
+}
+
+static void t_shconfig2(const char *file)
+{
+ const struct HXmap_node *node;
+ struct HXmap_trav *trav;
+ struct HXmap *map;
+
+ map = HX_shconfig_map(file);
+ if (map == NULL) {
+ fprintf(stderr, "HX_shconfig_map: %s\n", strerror(errno));
+ abort();
+ }
+ trav = HXmap_travinit(map, HXMAP_NOFLAGS);
+ while ((node = HXmap_traverse(trav)) != NULL)
+ printf("\t\"%s\" -> \"%s\"\n", node->skey, node->sdata);
+ HXmap_travfree(trav);
+}
+
+int main(int argc, const char **argv)
+{
+ int ret;
+
+ ret = HX_init();
+ if (ret <= 0) {
+ fprintf(stderr, "HX_init: %s\n", strerror(-ret));
+ return EXIT_FAILURE;
+ }
+ t_shconfig((argc >= 2) ? argv[1] : "tc-shconf.c");
+ t_shconfig2((argc >= 2) ? argv[1] : "tc-shconf.c");
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-strchr2.c b/src/tc-strchr2.c
new file mode 100644
index 0000000..6e0eaad
--- /dev/null
+++ b/src/tc-strchr2.c
@@ -0,0 +1,26 @@
+/*
+ * Behavior Correctness Test for HX_strchr2
+ * Copyright Jan Engelhardt, 2013
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <libHX/string.h>
+
+static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
+
+int main(void)
+{
+ char *z;
+
+ z = HX_strchr2("qfm!bar", alphabet);
+ if (z == NULL || *z != '!')
+ return EXIT_FAILURE;
+
+ z = HX_strchr2("qfmxbar", alphabet);
+ if (z != NULL)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-string.c b/src/tc-string.c
new file mode 100644
index 0000000..826a9a0
--- /dev/null
+++ b/src/tc-string.c
@@ -0,0 +1,228 @@
+/* long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing long line for testing */
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#ifndef __cplusplus
+# include <assert.h>
+# include <errno.h>
+# include <stddef.h>
+# include <stdio.h>
+# include <stdlib.h>
+#else
+# include <cassert>
+# include <cerrno>
+# include <cstddef>
+# include <cstdio>
+# include <cstdlib>
+#endif
+#include <libHX/defs.h>
+#include <libHX/init.h>
+#include <libHX/misc.h>
+#include <libHX/string.h>
+#include "internal.h"
+
+static void t_mc(void)
+{
+ hxmc_t *s, *old_s;
+
+ s = HXmc_meminit(NULL, 4096);
+ printf("%" HX_SIZET_FMT "u\n", HXmc_length(s));
+ if (HXmc_length(s) != 0)
+ abort();
+ old_s = s;
+ HXmc_trunc(&s, 8192);
+ if (old_s != s)
+ fprintf(stderr, "INFO: HXmc: no reallocation took place.\n");
+ printf("Length is now %" HX_SIZET_FMT "u\n", HXmc_length(s));
+ HXmc_setlen(&s, 16384);
+ printf("Length is now %" HX_SIZET_FMT "u\n", HXmc_length(s));
+ HXmc_free(s);
+}
+
+static void t_path(void)
+{
+ static const char *const paths[] = {
+ ".", "..", "/", "//", "/.", "/./", "/.//", "/./.",
+ "/mnt", "//mnt", "/mnt/", "//mnt/", "//mnt//", "//mnt//root",
+ "/mnt/.", "/mnt/./", "mnt", "mnt/", "mnt/root", "mnt/root/",
+ "mnt//root", NULL
+ };
+ const char *const *iter;
+
+ printf("#\tname\tbn\tbne\tdn\n");
+ for (iter = paths; *iter != NULL; ++iter) {
+ char *bn = HX_basename(*iter);
+ char *bne = HX_basename_exact(*iter);
+ char *dn = HX_dirname(*iter);
+ printf("\t%s\t%s\t%s\t%s\n", *iter, bn, bne, dn);
+ free(bne);
+ free(dn);
+ }
+}
+
+static void t_strcpy(void)
+{
+ hxmc_t *vp = NULL;
+
+ HXmc_strcpy(&vp, NULL);
+ if (vp != NULL)
+ abort();
+}
+
+static void t_strdup(void)
+{
+ char *a;
+ a = HX_strndup("DATA", 2);
+ printf(">%s<\n", a);
+ free(a);
+ a = HX_strndup("DATA", 10);
+ printf(">%s<\n", a);
+ free(a);
+}
+
+static void t_strncat(void)
+{
+ char data[5] = "DATA";
+
+ if (snprintf(data, sizeof(data), "12345678") >=
+ static_cast(ssize_t, sizeof(data)))
+ printf("Not enough space\n");
+ printf("String: >%s<\n", data);
+
+ HX_strlcat(data, "pqrstuv__", 2);
+ printf("String: >%s<\n", data);
+
+ *data = '\0';
+ HX_strlcat(data, "123456789", sizeof(data));
+ printf("String: >%s<\n", data);
+
+ *data = '\0';
+ HX_strlncat(data, "123456789", sizeof(data), 9);
+ printf("String: >%s<\n", data);
+}
+
+static void t_strnlen(void)
+{
+ static const char s[] = "Hello world";
+ printf("# strnlen: %" HX_SIZET_FMT "u %" HX_SIZET_FMT "u "
+ "%" HX_SIZET_FMT "u %" HX_SIZET_FMT "u %" HX_SIZET_FMT "u\n",
+ HX_strnlen(s, -1), HX_strnlen(s, 0), HX_strnlen(s, 1),
+ HX_strnlen(s, strlen(s)), HX_strnlen(s, 999));
+}
+
+static void t_strsep(void)
+{
+ char b[] = "jengelh:x:1500:100:Jan Engelhardt:/home/jengelh:/bin/bash";
+ char *wp = b, *ret;
+
+ printf("# strsep\n");
+ while ((ret = HX_strsep2(&wp, ":")) != NULL)
+ printf("%s\n", ret);
+}
+
+static void t_strtrim(void)
+{
+ char a[] = " a and b ", aexp[] = "a and b ";
+ char b[] = " a and b ", bexp[] = " a and b";
+ char c[] = "a&b", cexp[] = "a&b";
+ const char *r;
+
+ r = HX_stpltrim(a);
+ printf("HX_stpltrim(\"%s\") = \"%s\"\n", a, r);
+ assert(strcmp(r, aexp) == 0);
+
+ printf("HX_strltrim(\"%s\") = ", a);
+ printf("\"%s\"\n", (HX_strltrim(a), a));
+ assert(strcmp(a, aexp) == 0);
+
+ printf("HX_strrtrim(\"%s\") = ", b);
+ printf("\"%s\"\n", (HX_strrtrim(b), b));
+ assert(strcmp(b, bexp) == 0);
+
+ assert(strcmp(cexp, HX_stpltrim(c)) == 0);
+ assert(strcmp(cexp, (HX_strltrim(c), c)) == 0);
+ assert(strcmp(cexp, (HX_strrtrim(c), c)) == 0);
+}
+
+static void t_split(void)
+{
+ char t1[] = "root:x:0:0:root:/root:/bin/bash";
+ char t2[sizeof(t1)];
+ int f0, f1, f2;
+ char **a0, **a1, *a2[10];
+ char *const *wp;
+
+ memcpy(t2, t1, sizeof(t1));
+ a0 = HX_split(t1, ":", &f0, 0);
+ a1 = HX_split4(t1, ":", &f1, 0);
+ f2 = HX_split5(t2, ":", ARRAY_SIZE(a2), a2);
+
+ /* complete allocation */
+ printf("HX_split1: a0[%p]:", a0);
+ for (wp = a0; *wp != NULL; ++wp)
+ printf(" %s[%p]", *wp, *wp);
+ printf("\n");
+
+ /* array allocated */
+ printf("HX_split4: a1[%p]:", a1);
+ for (wp = a1; *wp != NULL; ++wp)
+ printf(" %s[%p]", *wp, *wp);
+ printf("\n");
+
+ /* nothing allocated */
+ printf("HX_split5: a2[%p]:", a2);
+ for (wp = a2; f2 > 0; --f2, ++wp)
+ printf(" %s[%p]", *wp, *wp);
+ printf("\n");
+
+ HX_zvecfree(a0);
+ free(a1);
+}
+
+static void t_split2(void)
+{
+ static const char tmp[] = "";
+ int c = 0;
+ char **a;
+
+ a = HX_split(tmp, " ", &c, 6);
+ printf("Got %d fields\n", c);
+ HX_zvecfree(a);
+}
+
+int main(int argc, const char **argv)
+{
+ hxmc_t *tx = NULL;
+ const char *file = (argc >= 2) ? argv[1] : "tx-string.cpp";
+ FILE *fp;
+
+ if (HX_init() <= 0)
+ abort();
+
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Cannot open %s: %s\n", file, strerror(errno));
+ } else {
+ while (HX_getl(&tx, fp) != NULL)
+ printf("%s", tx);
+ fclose(fp);
+ }
+
+ t_mc();
+ t_path();
+ t_strcpy();
+ t_strncat();
+ t_strnlen();
+ t_strdup();
+ t_strsep();
+ t_strtrim();
+ t_split();
+ t_split2();
+ HXmc_free(tx);
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-strquote.c b/src/tc-strquote.c
new file mode 100644
index 0000000..f26ff54
--- /dev/null
+++ b/src/tc-strquote.c
@@ -0,0 +1,70 @@
+/*
+ * Behavior Correctness Test for HX_strquote
+ * Copyright Jan Engelhardt, 2013
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libHX/string.h>
+
+static const char input1[] = "\"Good\" ol' \\'escaped\\' strings";
+static const char output1a[] = "\"Good\" ol\\' \\\\\\'escaped\\\\\\' strings";
+static const char output1b[] = "\\\"Good\\\" ol' \\\\'escaped\\\\' strings";
+static const char output1c[] = "\"Good\" ol'' \\''escaped\\'' strings";
+static const char input2[] = "<p style=\"height: 1;\">Foo &amp; \"bar\"</p>";
+static const char output2[] =
+ "&lt;p style=&quot;height: 1;&quot;&gt;Foo &amp;amp; &quot;bar&quot;&lt;/p&gt;";
+static const char input3[] = " #o=foo(*),ba\\r ";
+static const char output3a[] = " #o=foo\\28\\2A\\29,ba\\5Cr ";
+static const char output3b[] = "\\20\\23o\\3Dfoo(*)\\2Cba\\5Cr\\20";
+static const char output3c[] = "ICNvPWZvbygqKSxiYVxyIA==";
+static const char input4[] = "http://user:pass@host.de/~path/file(msvc);stuff.php?query[phpindex]=value&another=one;stuff";
+static const char output4[] = "http%3A%2F%2Fuser%3Apass%40host.de%2F~path%2Ffile%28msvc%29%3Bstuff.php%3Fquery%5Bphpindex%5D%3Dvalue%26another%3Done%3Bstuff";
+static const char input5[] = "echo hello `echo world`";
+static const char output5[] = "echo hello ``echo world``";
+
+static int test(const char *input, unsigned int mode, const char *expect)
+{
+ char *output = HX_strquote(input, mode, NULL);
+
+ if (output == NULL) {
+ fprintf(stderr, "HX_strquote returned NULL\n");
+ return EXIT_FAILURE;
+ }
+ if (strcmp(output, expect) != 0) {
+ fprintf(stderr, "Input: %s\nOutput: %s\nExpected: %s\n",
+ input, output, expect);
+ free(output);
+ return EXIT_FAILURE;
+ }
+ free(output);
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+#define tst(a, b, c) \
+ do { \
+ int ret = test((a), (b), (c)); \
+ if (ret != EXIT_SUCCESS) \
+ return ret; \
+ } while (false);
+
+ if (HX_strquote(input1, ~0U, NULL) != NULL)
+ return EXIT_FAILURE;
+ tst(input1, HXQUOTE_SQUOTE, output1a);
+ tst(input1, HXQUOTE_DQUOTE, output1b);
+ tst(input1, HXQUOTE_SQLSQUOTE, output1c);
+ tst(input2, HXQUOTE_HTML, output2);
+ tst(input3, HXQUOTE_LDAPFLT, output3a);
+ tst(input3, HXQUOTE_LDAPRDN, output3b);
+ tst(input3, HXQUOTE_BASE64, output3c);
+ tst(input4, HXQUOTE_URIENC, output4);
+ tst(input5, HXQUOTE_SQLBQUOTE, output5);
+ return 0;
+#undef tst
+}
diff --git a/src/tc-time.c b/src/tc-time.c
new file mode 100644
index 0000000..e842e77
--- /dev/null
+++ b/src/tc-time.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright Jan Engelhardt, 2012
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <libHX/defs.h>
+#include <libHX/init.h>
+#include <libHX/misc.h>
+#include "internal.h"
+
+typedef struct timespec *(*add_func_t)(struct timespec *,
+ const struct timespec *, const struct timespec *);
+typedef struct timespec *(*mul_func_t)(struct timespec *,
+ const struct timespec *, int);
+typedef struct timespec *(*mulf_func_t)(struct timespec *,
+ const struct timespec *, double);
+
+static const int NANOSECOND = 1000000000;
+static const long long NANOSECOND_LL = 1000000000;
+static const unsigned int clock_id = CLOCK_THREAD_CPUTIME_ID;
+static const unsigned int step = 1000;
+static const unsigned int step_mul = 10000000;
+
+static const struct timespec pairs[] = {
+ {-1, 700000000}, {-1, 400000000}, {-1, 0},
+ {0, -700000000}, {0, -400000000}, {0, 0},
+ {0, 400000000}, {0, 700000000},
+ {1, 0}, {1, 400000000}, {1, 700000000},
+};
+
+/*
+ * Variant that uses full 64 bit division and is thus slower on
+ * a handful of hardware.
+ */
+static struct timespec *HX_timespec_add_FDIV(struct timespec *r,
+ const struct timespec *a, const struct timespec *b)
+{
+ long long p, q;
+
+ p = a->tv_sec * NANOSECOND_LL +
+ ((a->tv_sec >= 0) ? a->tv_nsec : -a->tv_nsec);
+ q = b->tv_sec * NANOSECOND_LL +
+ ((b->tv_sec >= 0) ? b->tv_nsec : -b->tv_nsec);
+
+ p += q;
+ r->tv_sec = p / NANOSECOND;
+ r->tv_nsec = p % NANOSECOND;
+ if (r->tv_sec < 0 && r->tv_nsec < 0)
+ r->tv_nsec = -r->tv_nsec;
+ return r;
+}
+
+/*
+ * Variant that does split multiplication.
+ */
+static struct timespec *
+HX_timespec_mul_SPL(struct timespec *r, const struct timespec *a, int f)
+{
+ long long nsec;
+ bool neg = HX_timespec_isneg(a);
+
+ if (neg)
+ HX_timespec_neg(r, a);
+ else
+ *r = *a;
+ if (f < 0) {
+ f = -f;
+ neg = !neg;
+ }
+
+ r->tv_sec *= f;
+ nsec = static_cast(long long, r->tv_nsec) * f;
+ r->tv_sec += nsec / NANOSECOND;
+ r->tv_nsec = nsec % NANOSECOND;
+ if (neg)
+ HX_timespec_neg(r, r);
+ return r;
+}
+
+/*
+ * Variant for mulf that uses seconds rather than nanosecond as working base.
+ * This shows itself to be detrimental to precision (on IEEE-754).
+ */
+static struct timespec *
+HX_timespec_mulf_S(struct timespec *r, const struct timespec *a, double f)
+{
+ double i, t;
+
+ t = ((a->tv_sec >= 0) ? a->tv_nsec : -a->tv_nsec) /
+ static_cast(double, NANOSECOND);
+ t += a->tv_sec;
+ t *= f;
+ t = modf(t, &i);
+ r->tv_nsec = t * NANOSECOND;
+ r->tv_sec = i;
+ if (r->tv_sec < 0 && r->tv_nsec < 0)
+ r->tv_nsec = -r->tv_nsec;
+ return r;
+}
+
+static void test_same(void)
+{
+ static const struct timespec zero = {0, 0};
+ struct timespec r;
+ unsigned int i;
+
+ printf("# Test src==dst operand behavior\n");
+
+ /* 1s */
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ r = pairs[i];
+ printf("-(" HX_TIMESPEC_FMT ") = ", HX_TIMESPEC_EXP(&r));
+ HX_timespec_neg(&r, &r);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+ }
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ r = pairs[i];
+ printf(HX_TIMESPEC_FMT " + 0 = ", HX_TIMESPEC_EXP(&r));
+ HX_timespec_add(&r, &r, &zero);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+ }
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ r = pairs[i];
+ printf(HX_TIMESPEC_FMT " - 0 = ", HX_TIMESPEC_EXP(&r));
+ HX_timespec_sub(&r, &r, &zero);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+ }
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ r = pairs[i];
+ printf(HX_TIMESPEC_FMT " * 1 = ", HX_TIMESPEC_EXP(&r));
+ HX_timespec_mul(&r, &r, 1);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+ }
+ printf("\n");
+
+ /* 2s */
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ r = pairs[i];
+ printf(HX_TIMESPEC_FMT " + " HX_TIMESPEC_FMT " = ",
+ HX_TIMESPEC_EXP(&r), HX_TIMESPEC_EXP(&r));
+ HX_timespec_add(&r, &r, &r);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+ }
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ r = pairs[i];
+ printf(HX_TIMESPEC_FMT " - " HX_TIMESPEC_FMT " = ",
+ HX_TIMESPEC_EXP(&r), HX_TIMESPEC_EXP(&r));
+ HX_timespec_sub(&r, &r, &r);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+ }
+ printf("\n");
+}
+
+static void print_sgn(const struct timespec *a)
+{
+ printf(HX_timespec_isneg(a) ? "[-]" : "[+]");
+}
+
+static void test_neg(void)
+{
+ const struct timespec *now;
+ struct timespec then;
+
+ printf("# Negation\n");
+ for (now = pairs; now < pairs + ARRAY_SIZE(pairs); ++now) {
+ HX_timespec_neg(&then, now);
+
+ print_sgn(now);
+ printf(HX_TIMESPEC_FMT " -> ", HX_TIMESPEC_EXP(now));
+ print_sgn(&then);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&then));
+ }
+ printf("\n");
+}
+
+static void print_op2(const struct timespec *r, const struct timespec *a,
+ const char *op, const struct timespec *b)
+{
+ printf(HX_TIMESPEC_FMT " %s ", HX_TIMESPEC_EXP(a), op);
+ printf(HX_TIMESPEC_FMT " = ", HX_TIMESPEC_EXP(b));
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(r));
+}
+
+static void test_add(void)
+{
+ const struct timespec *a, *b;
+ struct timespec r, s;
+
+ printf("# Test addition behavior\n");
+ for (a = pairs; a < pairs + ARRAY_SIZE(pairs); ++a) {
+ for (b = pairs; b < pairs + ARRAY_SIZE(pairs); ++b) {
+ HX_timespec_add(&r, a, b);
+ print_op2(&r, a, "+N", b);
+ HX_timespec_add_FDIV(&s, a, b);
+ print_op2(&r, a, "+F", b);
+ if (r.tv_sec != s.tv_sec || r.tv_nsec != s.tv_nsec)
+ abort();
+ HX_timespec_sub(&r, a, b);
+ print_op2(&r, a, "- ", b);
+ printf("----------\n");
+ }
+ }
+ printf("\n");
+}
+
+static void test_adds_nz(time_t s, add_func_t fn)
+{
+ struct timespec a, b, r;
+
+ a.tv_sec = s;
+ for (a.tv_nsec = 0; a.tv_nsec < NANOSECOND;
+ a.tv_nsec += NANOSECOND / step)
+ {
+ b.tv_sec = -1;
+ for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
+ b.tv_nsec += NANOSECOND / step)
+ (*fn)(&r, &a, &b);
+
+ b.tv_sec = 0;
+ for (b.tv_nsec = -NANOSECOND + NANOSECOND / step;
+ b.tv_nsec < NANOSECOND; b.tv_nsec += NANOSECOND / step)
+ (*fn)(&r, &a, &b);
+
+ b.tv_sec = 1;
+ for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
+ b.tv_nsec += NANOSECOND / step)
+ (*fn)(&r, &a, &b);
+ }
+}
+
+static void test_adds_z(add_func_t fn)
+{
+ struct timespec a, b, r;
+
+ a.tv_sec = 0;
+ for (a.tv_nsec = -NANOSECOND + NANOSECOND / step;
+ a.tv_nsec < NANOSECOND; a.tv_nsec += NANOSECOND / step)
+ {
+ b.tv_sec = -1;
+ for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
+ b.tv_nsec += NANOSECOND / step)
+ (*fn)(&r, &a, &b);
+
+ b.tv_sec = 0;
+ for (b.tv_nsec = -NANOSECOND + NANOSECOND / step;
+ b.tv_nsec < NANOSECOND; b.tv_nsec += NANOSECOND / step)
+ (*fn)(&r, &a, &b);
+
+ b.tv_sec = 1;
+ for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
+ b.tv_nsec += NANOSECOND / step)
+ (*fn)(&r, &a, &b);
+ }
+}
+
+static void test_adds_1(const char *text, add_func_t fn)
+{
+ struct timespec start, delta;
+
+ printf("%s", text);
+ clock_gettime(clock_id, &start);
+ test_adds_nz(-2, fn);
+ test_adds_nz(-1, fn);
+ test_adds_z(fn);
+ test_adds_nz(1, fn);
+ test_adds_nz(2, fn);
+ clock_gettime(clock_id, &delta);
+ HX_timespec_sub(&delta, &delta, &start);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
+}
+
+static void test_adds(void)
+{
+ printf("# Test addition speed\n");
+ test_adds_1("normal: ", HX_timespec_add);
+ test_adds_1("fulldiv: ", HX_timespec_add_FDIV);
+ printf("\n");
+}
+
+static void test_mul(void)
+{
+ struct timespec r, s;
+ unsigned int i;
+ double k;
+ int j;
+
+ printf("# Test multiplication behavior\n");
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ for (j = -3; j <= 3; ++j) {
+ printf(HX_TIMESPEC_FMT " *N %d = ",
+ HX_TIMESPEC_EXP(&pairs[i]), j);
+ HX_timespec_mul(&r, &pairs[i], j);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+
+ printf(HX_TIMESPEC_FMT " *S %d = ",
+ HX_TIMESPEC_EXP(&pairs[i]), j);
+ HX_timespec_mul_SPL(&s, &pairs[i], j);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&s));
+
+ if (r.tv_sec != s.tv_sec || r.tv_nsec != s.tv_nsec)
+ abort();
+ }
+
+ for (k = -3; k <= 3; k += 0.1) {
+ printf(HX_TIMESPEC_FMT " *fN %f = ",
+ HX_TIMESPEC_EXP(&pairs[i]), k);
+ HX_timespec_mulf(&r, &pairs[i], k);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
+
+ printf(HX_TIMESPEC_FMT " *fS %f = ",
+ HX_TIMESPEC_EXP(&pairs[i]), k);
+ HX_timespec_mulf_S(&s, &pairs[i], k);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&s));
+ }
+ }
+
+ printf("\n");
+}
+
+static void test_muls_1i(const char *text, mul_func_t fn)
+{
+ struct timespec r, s, start, delta;
+ unsigned int i;
+
+ printf("%s", text);
+ clock_gettime(clock_id, &start);
+ for (i = 0; i < step_mul; ++i) {
+ r.tv_sec = -i;
+ r.tv_nsec = -i / 4;
+ (*fn)(&s, &r, 7);
+ }
+ clock_gettime(clock_id, &delta);
+ HX_timespec_sub(&delta, &delta, &start);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
+}
+
+static void test_muls_1f(const char *text, mulf_func_t fn)
+{
+ struct timespec r, s, start, delta;
+ unsigned int i;
+
+ printf("%s", text);
+ clock_gettime(clock_id, &start);
+ for (i = 0; i < step_mul; ++i) {
+ r.tv_sec = -i;
+ r.tv_nsec = -i / 4;
+ (*fn)(&s, &r, 7);
+ }
+ clock_gettime(clock_id, &delta);
+ HX_timespec_sub(&delta, &delta, &start);
+ printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
+}
+
+static void test_muls(void)
+{
+ printf("# Test multiplication speed\n");
+ test_muls_1i("normal: ", HX_timespec_mul);
+ test_muls_1i("split: ", HX_timespec_mul_SPL);
+
+ test_muls_1f("float: ", HX_timespec_mulf);
+ test_muls_1f("flt-S: ", HX_timespec_mulf_S);
+ printf("\n");
+}
+
+int main(void)
+{
+ if (HX_init() <= 0)
+ abort();
+
+ test_same();
+ test_neg();
+ test_add();
+ test_mul();
+ test_adds();
+ test_muls();
+ HX_exit();
+ return EXIT_SUCCESS;
+}
diff --git a/src/tc-xml.c b/src/tc-xml.c
new file mode 100644
index 0000000..07cf3b3
--- /dev/null
+++ b/src/tc-xml.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright Jan Engelhardt
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the WTF Public License version 2 or
+ * (at your option) any later version.
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <libxml/parser.h>
+#include <libHX/defs.h>
+#include <libHX/libxml_helper.h>
+
+int main(void)
+{
+ xmlDoc *doc;
+ xmlNode *root, *etc, *node;
+ char *result = NULL;
+ int size = 0;
+
+ doc = xmlNewDoc(NULL);
+ root = xmlNewDocNode(doc, NULL, "root", NULL);
+ xmlDocSetRootElement(doc, root);
+ xml_newnode(root, "empty", NULL);
+ etc = xml_newnode(root, "filled", NULL);
+ xml_newnode(etc, "a", "1234 bytes");
+ node = xml_newnode(etc, "b", "0 bytes");
+ xml_newnode(node, "extra", NULL);
+ xmlDocDumpFormatMemory(doc, reinterpret_cast(xmlChar **, &result),
+ &size, true);
+ xmlSaveFileEnc("test.xml", doc, "utf-8");
+ if (result != NULL)
+ printf("%.*s\n", size, result);
+ return 0;
+}
diff --git a/src/time.c b/src/time.c
new file mode 100644
index 0000000..3d25042
--- /dev/null
+++ b/src/time.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright Jan Engelhardt, 2012
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdbool.h>
+#include <time.h>
+#include <libHX/misc.h>
+#include "internal.h"
+
+#define MICROSECOND 100000
+#define NANOSECOND 1000000000
+#define NANOSECOND_LL 1000000000LL
+
+#ifdef HAVE_STRUCT_TIMESPEC_TV_NSEC
+EXPORT_SYMBOL bool HX_timespec_isneg(const struct timespec *x)
+{
+ return (x->tv_sec < 0) || (x->tv_nsec < 0);
+}
+
+EXPORT_SYMBOL struct timespec *
+HX_timespec_neg(struct timespec *r, const struct timespec *a)
+{
+ if (a->tv_sec != 0) {
+ r->tv_sec = -a->tv_sec;
+ r->tv_nsec = a->tv_nsec;
+ } else {
+ r->tv_sec = 0;
+ r->tv_nsec = -a->tv_nsec;
+ }
+ return r;
+}
+
+EXPORT_SYMBOL struct timespec *HX_timespec_add(struct timespec *r,
+ const struct timespec *a, const struct timespec *b)
+{
+ /*
+ * Split the value represented by the struct into two
+ * independent values that can be added individually.
+ */
+ long nsec[2];
+ nsec[0] = (a->tv_sec < 0) ? -a->tv_nsec : a->tv_nsec;
+ nsec[1] = (b->tv_sec < 0) ? -b->tv_nsec : b->tv_nsec;
+
+ r->tv_sec = a->tv_sec + b->tv_sec;
+ r->tv_nsec = nsec[0] + nsec[1];
+ if (r->tv_nsec >= NANOSECOND) {
+ ++r->tv_sec;
+ r->tv_nsec -= NANOSECOND;
+ } else if (r->tv_nsec <= -NANOSECOND) {
+ --r->tv_sec;
+ r->tv_nsec += NANOSECOND;
+ }
+
+ /* Combine again */
+ if (r->tv_sec < 0) {
+ if (r->tv_nsec < 0) {
+ r->tv_nsec = -r->tv_nsec;
+ } else if (r->tv_nsec > 0) {
+ if (++r->tv_sec == 0)
+ r->tv_nsec = -NANOSECOND + r->tv_nsec;
+ else
+ r->tv_nsec = NANOSECOND - r->tv_nsec;
+ }
+ } else if (r->tv_sec > 0 && r->tv_nsec < 0) {
+ --r->tv_sec;
+ r->tv_nsec = NANOSECOND + r->tv_nsec;
+ }
+ return r;
+}
+
+EXPORT_SYMBOL struct timespec *HX_timespec_sub(struct timespec *r,
+ const struct timespec *a, const struct timespec *b)
+{
+ struct timespec b2;
+ return HX_timespec_add(r, a, HX_timespec_neg(&b2, b));
+}
+
+EXPORT_SYMBOL void HX_diff_timespec(struct timespec *delta,
+ const struct timespec *future, const struct timespec *past)
+{
+ HX_timespec_sub(delta, future, past);
+}
+
+EXPORT_SYMBOL struct timespec *
+HX_timespec_mul(struct timespec *r, const struct timespec *a, int f)
+{
+ long long t;
+
+ t = a->tv_sec * NANOSECOND_LL +
+ ((a->tv_sec >= 0) ? a->tv_nsec : -a->tv_nsec);
+ t *= f;
+ r->tv_sec = t / NANOSECOND;
+ r->tv_nsec = t % NANOSECOND;
+ if (r->tv_sec < 0 && r->tv_nsec < 0)
+ r->tv_nsec = -r->tv_nsec;
+ return r;
+}
+
+EXPORT_SYMBOL struct timespec *
+HX_timespec_mulf(struct timespec *r, const struct timespec *a, double f)
+{
+ double t;
+
+ t = (a->tv_sec * NANOSECOND_LL +
+ ((a->tv_sec >= 0) ? a->tv_nsec : -a->tv_nsec)) * f;
+ r->tv_sec = t / NANOSECOND;
+ /*
+ * This is quite the same as r->tv_nsec = fmod(t, NANOSECOND),
+ * except that without the library call, we are faster.
+ */
+ r->tv_nsec = t - r->tv_sec * NANOSECOND_LL;
+ if (r->tv_sec < 0 && r->tv_nsec < 0)
+ r->tv_nsec = -r->tv_nsec;
+ return r;
+}
+#endif
+
+#ifdef HAVE_STRUCT_TIMEVAL_TV_USEC
+EXPORT_SYMBOL struct timeval *HX_timeval_sub(struct timeval *delta,
+ const struct timeval *future, const struct timeval *past)
+{
+ delta->tv_sec = future->tv_sec - past->tv_sec;
+ delta->tv_usec = future->tv_usec - past->tv_usec;
+ if (future->tv_sec < past->tv_sec || (future->tv_sec == past->tv_sec &&
+ future->tv_usec < past->tv_usec)) {
+ if (future->tv_usec > past->tv_usec) {
+ delta->tv_usec = -MICROSECOND + delta->tv_usec;
+ ++delta->tv_sec;
+ }
+ if (delta->tv_sec < 0)
+ delta->tv_usec *= -1;
+ } else if (delta->tv_usec < 0) {
+ delta->tv_usec += MICROSECOND;
+ --delta->tv_sec;
+ }
+ return delta;
+}
+
+EXPORT_SYMBOL void HX_diff_timeval(struct timeval *delta,
+ const struct timeval *future, const struct timeval *past)
+{
+ HX_timeval_sub(delta, future, past);
+}
+#endif
+
+EXPORT_SYMBOL long HX_time_compare(const struct stat *a,
+ const struct stat *b, char sel)
+{
+ long r;
+
+#if defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
+ if (sel == 'm')
+ return ((r = a->st_mtime - b->st_mtime) != 0) ?
+ r : a->st_mtimensec - b->st_mtimensec;
+#ifdef HAVE_STRUCT_STAT_ST_OTIMENSEC
+ else if (sel == 'o')
+ return ((r = a->st_otime - b->st_otime) != 0) ?
+ r : a->st_otimensec - b->st_otimensec;
+#endif
+ else if (sel == 'a')
+ return ((r = a->st_atime - b->st_atime) != 0) ?
+ r : a->st_atimensec - b->st_atimensec;
+ else if (sel == 'c')
+ return ((r = a->st_ctime - b->st_ctime) != 0) ?
+ r : a->st_ctimensec - b->st_ctimensec;
+#elif defined(HAVE_STRUCT_STAT_ST_MTIM)
+ if (sel == 'm')
+ return ((r = a->st_mtim.tv_sec - b->st_mtim.tv_sec) != 0) ?
+ r : a->st_mtim.tv_nsec - b->st_mtim.tv_nsec;
+#ifdef HAVE_STRUCT_STAT_ST_OTIM
+ else if (sel == 'o')
+ return ((r = a->st_otim.tv_sec - b->st_otim.tv_sec) != 0) ?
+ r : a->st_otim.tv_nsec - b->st_otim.tv_nsec;
+#endif
+ else if (sel == 'a')
+ return ((r = a->st_atim.tv_sec - b->st_atim.tv_sec) != 0) ?
+ r : a->st_atim.tv_nsec - b->st_atim.tv_nsec;
+ else if (sel == 'c')
+ return ((r = a->st_ctim.tv_sec - b->st_ctim.tv_sec) != 0) ?
+ r : a->st_ctim.tv_nsec - b->st_ctim.tv_nsec;
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
+ if (sel == 'm')
+ return ((r = a->st_mtimespec.tv_sec - b->st_mtimespec.tv_sec) != 0) ?
+ r : a->st_mtimespec.tv_nsec - b->st_mtimespec.tv_nsec;
+#ifdef HAVE_STRUCT_STAT_ST_OTIMESPEC
+ else if (sel == 'o')
+ return ((r = a->st_otimespec.tv_sec - b->st_otimespec.tv_sec) != 0) ?
+ r : a->st_otimespec.tv_nsec - b->st_otimespec.tv_nsec;
+#endif
+ else if (sel == 'a')
+ return ((r = a->st_atimespec.tv_sec - b->st_atimespec.tv_sec) != 0) ?
+ r : a->st_atimespec.tv_nsec - b->st_atimespec.tv_nsec;
+ else if (sel == 'c')
+ return ((r = a->st_ctimespec.tv_sec - b->st_ctimespec.tv_sec) != 0) ?
+ r : a->st_ctimespec.tv_nsec - b->st_ctimespec.tv_nsec;
+#elif defined(HAVE_STRUCT_STAT_ST_MTIME)
+ if (sel == 'm')
+ return a->st_mtime - b->st_mtime;
+#ifdef HAVE_STRUCT_STAT_ST_OTIME
+ else if (sel == 'o')
+ return a->st_otime - b->st_otime;
+#endif
+ else if (sel == 'a')
+ return a->st_atime - b->st_atime;
+ else if (sel == 'c')
+ return a->st_ctime - b->st_ctime;
+#else
+# error Tis not ending well.
+#endif
+ return 0;
+}
diff --git a/src/tx-cast.cpp b/src/tx-cast.cpp
new file mode 100644
index 0000000..1dd7f1a
--- /dev/null
+++ b/src/tx-cast.cpp
@@ -0,0 +1 @@
+#include "tc-cast.c"
diff --git a/src/tx-compile.cpp b/src/tx-compile.cpp
new file mode 100644
index 0000000..2c21e3b
--- /dev/null
+++ b/src/tx-compile.cpp
@@ -0,0 +1 @@
+#include "tc-compile.c"
diff --git a/src/tx-deque.cpp b/src/tx-deque.cpp
new file mode 100644
index 0000000..5e00beb
--- /dev/null
+++ b/src/tx-deque.cpp
@@ -0,0 +1 @@
+#include "tc-deque.c"
diff --git a/src/tx-dir.cpp b/src/tx-dir.cpp
new file mode 100644
index 0000000..fac8f8f
--- /dev/null
+++ b/src/tx-dir.cpp
@@ -0,0 +1 @@
+#include "tc-dir.c"
diff --git a/src/tx-list.cpp b/src/tx-list.cpp
new file mode 100644
index 0000000..8507711
--- /dev/null
+++ b/src/tx-list.cpp
@@ -0,0 +1 @@
+#include "tc-list.c"
diff --git a/src/tx-list2.cpp b/src/tx-list2.cpp
new file mode 100644
index 0000000..07c4151
--- /dev/null
+++ b/src/tx-list2.cpp
@@ -0,0 +1 @@
+#include "tc-list2.c"
diff --git a/src/tx-misc.cpp b/src/tx-misc.cpp
new file mode 100644
index 0000000..8c61822
--- /dev/null
+++ b/src/tx-misc.cpp
@@ -0,0 +1 @@
+#include "tc-misc.c"
diff --git a/src/tx-netio.cpp b/src/tx-netio.cpp
new file mode 100644
index 0000000..d4c9c31
--- /dev/null
+++ b/src/tx-netio.cpp
@@ -0,0 +1 @@
+#include "tc-netio.c"
diff --git a/src/tx-option.cpp b/src/tx-option.cpp
new file mode 100644
index 0000000..55e3566
--- /dev/null
+++ b/src/tx-option.cpp
@@ -0,0 +1,17 @@
+#ifndef __cplusplus
+# include <stdlib.h>
+#else
+# include <cstdlib>
+#endif
+#include <libHX/option.h>
+
+static const struct HXoption t[] = {
+ HXOPT_AUTOHELP,
+ HXOPT_TABLEEND,
+};
+
+int main(int argc, const char **argv)
+{
+ HX_getopt(t, &argc, &argv, HXOPT_USAGEONERR);
+ return EXIT_SUCCESS;
+}
diff --git a/src/tx-proc.cpp b/src/tx-proc.cpp
new file mode 100644
index 0000000..4c1006d
--- /dev/null
+++ b/src/tx-proc.cpp
@@ -0,0 +1 @@
+#include "tc-proc.c"
diff --git a/src/tx-rand.cpp b/src/tx-rand.cpp
new file mode 100644
index 0000000..e240ea1
--- /dev/null
+++ b/src/tx-rand.cpp
@@ -0,0 +1 @@
+#include "tc-rand.c"
diff --git a/src/tx-strchr2.cpp b/src/tx-strchr2.cpp
new file mode 100644
index 0000000..8e2dc4e
--- /dev/null
+++ b/src/tx-strchr2.cpp
@@ -0,0 +1 @@
+#include "tc-strchr2.c"
diff --git a/src/tx-string.cpp b/src/tx-string.cpp
new file mode 100644
index 0000000..6abf6c1
--- /dev/null
+++ b/src/tx-string.cpp
@@ -0,0 +1 @@
+#include "tc-string.c"
diff --git a/src/tx-strquote.cpp b/src/tx-strquote.cpp
new file mode 100644
index 0000000..6742e2c
--- /dev/null
+++ b/src/tx-strquote.cpp
@@ -0,0 +1 @@
+#include "tc-strquote.c"
diff --git a/src/tx-time.cpp b/src/tx-time.cpp
new file mode 100644
index 0000000..ec1ef1b
--- /dev/null
+++ b/src/tx-time.cpp
@@ -0,0 +1 @@
+#include "tc-time.c"
diff --git a/src/ux-file.c b/src/ux-file.c
new file mode 100644
index 0000000..383f169
--- /dev/null
+++ b/src/ux-file.c
@@ -0,0 +1,48 @@
+#include <errno.h>
+#include <stddef.h>
+#include "internal.h"
+
+EXPORT_SYMBOL int chown(const char *path, long uid, long gid)
+{
+ return -(errno = ENOSYS);
+}
+
+EXPORT_SYMBOL int fchmod(int fd, long perm)
+{
+ return -(errno = ENOSYS);
+}
+
+EXPORT_SYMBOL int fchown(int fd, long uid, long gid)
+{
+ return -(errno = ENOSYS);
+}
+
+EXPORT_SYMBOL int lchown(const char *path, long uid, long gid)
+{
+ return -(errno = ENOSYS);
+}
+
+EXPORT_SYMBOL int lstat(const char *path, struct stat *sb)
+{
+ return stat(path, sb);
+}
+
+EXPORT_SYMBOL int mkfifo(const char *path, long mode)
+{
+ return -(errno = EPERM);
+}
+
+EXPORT_SYMBOL int mknod(const char *path, long mode, long dev)
+{
+ return -(errno = EPERM);
+}
+
+EXPORT_SYMBOL int readlink(const char *path, char *dest, size_t len)
+{
+ return -(errno = EINVAL);
+}
+
+EXPORT_SYMBOL int symlink(const char *src, const char *dest)
+{
+ return -(errno = EPERM);
+}
diff --git a/src/ux-mmap.c b/src/ux-mmap.c
new file mode 100644
index 0000000..35b9aba
--- /dev/null
+++ b/src/ux-mmap.c
@@ -0,0 +1,79 @@
+/*
+ * libHX/ux-mmap.c
+ * Copyright Jan Engelhardt, 2005-2006
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include <sys/types.h>
+#include <io.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <windows.h>
+#include "internal.h"
+#include <libHX/misc.h>
+
+static __inline__ DWORD dw_desired_access(int, int);
+static __inline__ DWORD fl_protect(int, int);
+
+EXPORT_SYMBOL void *mmap(void *start, size_t length, int prot, int flags,
+ int fd, off_t offset)
+{
+ HANDLE filp, fmap;
+ void *p;
+
+ filp = reinterpret_cast(HANDLE, _get_osfhandle(fd));
+ fmap = CreateFileMapping(filp, NULL, fl_protect(prot, flags),
+ 0, 0, NULL);
+ if (fmap == NULL)
+ return MAP_FAILED;
+
+ p = MapViewOfFile(fmap, dw_desired_access(prot, flags),
+ ((int64_t)offset >> 32) & 0xFFFFFFFFUL,
+ offset & 0xFFFFFFFFUL, length);
+ CloseHandle(fmap);
+ if (p == NULL)
+ return MAP_FAILED;
+
+ return p;
+}
+
+EXPORT_SYMBOL int munmap(void *start, size_t length)
+{
+ if (!UnmapViewOfFile(start))
+ return -1;
+ return 0;
+}
+
+static __inline__ DWORD dw_desired_access(int prot, int flags)
+{
+ if (flags & MAP_PRIVATE) return FILE_MAP_COPY;
+ if (prot & PROT_WRITE) return FILE_MAP_WRITE;
+ if (prot & PROT_READ) return FILE_MAP_READ;
+#ifdef FILE_MAP_EXECUTE /* WinXP SP2 or WinServer2003 SP1 */
+ if (prot & PROT_EXEC) return FILE_MAP_EXECUTE;
+#endif
+ return 0;
+}
+
+static __inline__ DWORD fl_protect(int prot, int flags)
+{
+ if (flags & MAP_PRIVATE)
+ return PAGE_WRITECOPY;
+#ifdef PAGE_EXECUTE_READ
+ if (flags & (PROT_EXEC | PROT_READ))
+ return PAGE_EXECUTE_READ;
+#endif
+#ifdef PAGE_EXECUTE_READWRITE
+ if (flags & (PROT_EXEC | PROT_READ | PROT_WRITE))
+ return PAGE_EXECUTE_READWRITE;
+#endif
+ if (flags & PROT_WRITE)
+ return PAGE_READWRITE;
+ if (flags & PROT_READ)
+ return PAGE_READONLY;
+ return 0;
+}
diff --git a/src/uxcompat.h b/src/uxcompat.h
new file mode 100644
index 0000000..e735c50
--- /dev/null
+++ b/src/uxcompat.h
@@ -0,0 +1,104 @@
+#ifndef _LIBHX_UXCOMPAT_H
+#define _LIBHX_UXCOMPAT_H 1
+
+#if defined(__cplusplus) && __cplusplus >= 201100UL
+# include <cstddef>
+# include <cstdint>
+#else
+# include <stddef.h>
+# include <stdint.h>
+#endif
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ENOSYS
+# define ENOSYS 38 /* Function not implemented */
+#endif
+
+#ifndef S_IFLNK
+# define S_IFLNK 0xA000
+#endif
+#ifndef S_IFSOCK
+# define S_IFSOCK 0xC000
+#endif
+#ifndef S_IFBLK
+# define S_IFBLK 0x6000
+#endif
+#ifndef S_IFCHR
+# define S_IFCHR 0x2000
+#endif
+#ifndef S_IFIFO
+# define S_IFIFO 0x1000
+#endif
+#ifndef S_ISBLK
+# define S_ISBLK(__mode) (((__mode) & S_IFMT) == S_IFBLK)
+#endif
+#ifndef S_ISCHR
+# define S_ISCHR(__mode) (((__mode) & S_IFMT) == S_IFCHR)
+#endif
+#ifndef S_ISDIR
+# define S_ISDIR(__mode) (((__mode) & S_IFMT) == S_IFDIR)
+#endif
+#ifndef S_ISREG
+# define S_ISREG(__mode) (((__mode) & S_IFMT) == S_IFREG)
+#endif
+#ifndef S_ISLNK
+# define S_ISLNK(__mode) (((__mode) & S_IFMT) == S_IFLNK)
+#endif
+#ifndef S_ISFIFO
+# define S_ISFIFO(__mode) (((__mode) & S_IFMT) == S_IFIFO)
+#endif
+#ifndef S_ISSOCK
+# define S_ISSOCK(__mode) (((__mode) & S_IFMT) == S_IFSOCK)
+#endif
+#ifndef S_IRGRP
+# define S_IRGRP 00040
+#endif
+#ifndef S_IWGRP
+# define S_IWGRP 00020
+#endif
+#ifndef S_IROTH
+# define S_IROTH 00004
+#endif
+#ifndef S_IWOTH
+# define S_IWOTH 00002
+#endif
+
+struct stat;
+
+/*
+ * UX-FILE.C
+ */
+extern int chown(const char *, long, long);
+extern int fchmod(int, long);
+extern int fchown(int, long, long);
+extern int lchown(const char *, long, long);
+extern int lstat(const char *, struct stat *);
+extern int mkfifo(const char *, long);
+extern int mknod(const char *, long, long);
+extern int readlink(const char *, char *, size_t);
+extern int symlink(const char *, const char *);
+
+/*
+ * UX-MMAP.C
+ */
+#ifdef _WIN32
+# define MAP_FAILED reinterpret_cast(void *, static_cast(intptr_t, -1))
+# define PROT_NONE 0x0
+# define PROT_READ 0x1
+# define PROT_WRITE 0x2
+# define PROT_EXEC 0x4
+# define MAP_SHARED 0x1
+# define MAP_PRIVATE 0x2
+extern void *mmap(void *, size_t, int, int, int, off_t);
+extern int munmap(void *, size_t);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* _LIBHX_UXCOMPAT_H */