CORE_CFLAGS = `$(switch_builddir)/libs/apr/apr-1-config --cflags --cppflags --includes`
CORE_CFLAGS += `$(switch_builddir)/libs/apr-util/apu-1-config --includes`
+CORE_CFLAGS += -I$(switch_srcdir)/libs/libtpl-1.5/src
CORE_CFLAGS += -I$(switch_srcdir)/libs/stfu
CORE_CFLAGS += -I$(switch_builddir)/libs/sqlite
CORE_CFLAGS += -I$(switch_srcdir)/libs/pcre
libs/libteletone/src/libteletone_detect.h \
libs/libteletone/src/libteletone_generate.h \
libs/libteletone/src/libteletone.h \
+ libs/libtpl-1.5/src/tpl.h \
src/include/switch_limit.h \
src/include/switch_odbc.h
src/switch_profile.c \
src/switch_json.c \
src/switch_curl.c \
+ libs/libtpl-1.5/src/tpl.c \
libs/stfu/stfu.c \
libs/libteletone/src/libteletone_detect.c \
libs/libteletone/src/libteletone_generate.c \
--- /dev/null
+Copyright (c) 2005-2010, Troy Hanson http://tpl.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
--- /dev/null
+SUBDIRS = src
+EXTRA_DIST = LICENSE tests lang doc
--- /dev/null
+# Makefile.in generated by automake 1.10.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 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@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@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@
+subdir = .
+DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(top_srcdir)/config/config.h.in \
+ $(top_srcdir)/configure config/config.guess config/config.sub \
+ config/depcomp config/install-sh config/ltmain.sh \
+ config/missing
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config/config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ { test ! -d $(distdir) \
+ || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -fr $(distdir); }; }
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+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@
+MAKEINFO = @MAKEINFO@
+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_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_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+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@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = src
+EXTRA_DIST = LICENSE tests lang doc
+all: all-recursive
+
+.SUFFIXES:
+am--refresh:
+ @:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \
+ cd $(srcdir) && $(AUTOMAKE) --foreign \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ echo ' $(SHELL) ./config.status'; \
+ $(SHELL) ./config.status;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+
+config/config.h: config/stamp-h1
+ @if test ! -f $@; then \
+ rm -f config/stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) config/stamp-h1; \
+ else :; fi
+
+config/stamp-h1: $(top_srcdir)/config/config.h.in $(top_builddir)/config.status
+ @rm -f config/stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status config/config.h
+$(top_srcdir)/config/config.h.in: $(am__configure_deps)
+ cd $(top_srcdir) && $(AUTOHEADER)
+ rm -f config/stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config/config.h config/stamp-h1
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool config.lt
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(am__remove_distdir)
+ test -d $(distdir) || mkdir $(distdir)
+ @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 $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r $(distdir)
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+dist-lzma: distdir
+ tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+ $(am__remove_distdir)
+
+dist-tarZ: distdir
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__remove_distdir)
+
+dist-shar: distdir
+ shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ $(am__remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__remove_distdir)
+
+dist dist-all: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.lzma*) \
+ unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ esac
+ chmod -R a-w $(distdir); chmod a+w $(distdir)
+ mkdir $(distdir)/_build
+ mkdir $(distdir)/_inst
+ chmod a-w $(distdir)
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && cd $(distdir)/_build \
+ && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
+ $(am__remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+ @cd $(distuninstallcheck_dir) \
+ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+ install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am am--refresh check check-am clean clean-generic \
+ clean-libtool ctags ctags-recursive dist dist-all dist-bzip2 \
+ dist-gzip dist-lzma dist-shar dist-tarZ dist-zip distcheck \
+ distclean distclean-generic distclean-hdr distclean-libtool \
+ distclean-tags distcleancheck distdir distuninstallcheck 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-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-recursive uninstall uninstall-am
+
+# 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:
--- /dev/null
+tpl: fast, easy serialization in C
+==============================================================================
+
+Documentation for tpl is available in the doc/ directory or at:
+
+ http://tpl.sourceforge.net
+
+You can build tpl as a library, like so:
+
+ ./configure
+ make
+ make install
+
+This installs libtpl.so and libtpl.a into a standard system library directory.
+You can customize the install directory using configure's "--prefix" option:
+
+ ./configure --prefix=/some/directory
+
+For other options accepted by configure, run "./configure --help".
+
+NON-LIBRARY OPTION
+------------------
+Alternatively, if you don't want to muck around with libraries, you can simply
+copy these two files into your own C project and build them with your program:
+
+ src/tpl.h
+ src/tpl.c
+
+WINDOWS
+-------
+You can build tpl as a DLL under Visual Studio 2008. Or you can use MinGW or
+Cygwin.
+
+SELF-TEST SUITE
+---------------
+The automated self-test can be run by doing:
+
+ cd tests
+ make
+
+LICENSE
+-------
+The BSD license applies to this software. The text is in the LICENSE file.
+
+CREDITS
+-------
+Many people have contributed to tpl, both bits of code and ideas. Rather than
+listing them all here, at risk of omitting anyone- I just wish to say thank
+you. Some particular features are noted with contributors' names in the
+ChangeLog.
+
+Feel free to send me questions, comments or bug reports.
+
+Troy D. Hanson, February 5, 2010
+thanson@users.sourceforge.net
+
--- /dev/null
+#!/bin/sh
+
+# THIS SCRIPT IS FOR PROJECT MAINTAINER ONLY
+# It is executed only to generate "configure"
+
+set -x
+aclocal -I config
+autoheader
+libtoolize --copy --force
+automake --foreign --add-missing --copy
+autoconf
--- /dev/null
+AC_PREREQ(2.59)
+
+AC_INIT([libtpl], [1.4], [thanson@users.sourceforge.net])
+AC_CONFIG_SRCDIR(src/tpl.c)
+AC_CONFIG_AUX_DIR(config)
+AC_CONFIG_HEADERS(config/config.h)
+AM_INIT_AUTOMAKE
+AC_PROG_CC
+dnl next 4 lines are a hack to avoid libtool's
+dnl needless checks for C++ and Fortran compilers
+m4_undefine([AC_PROG_CXX])
+m4_defun([AC_PROG_CXX],[])
+m4_undefine([AC_PROG_F77])
+m4_defun([AC_PROG_F77],[])
+AC_PROG_LIBTOOL
+
+dnl detect Cygwin or MinGW and use mmap family replacements
+AC_CONFIG_LIBOBJ_DIR(src/win)
+case $host in
+ *-*-mingw32* | *-*-cygwin* | *-*-windows*)
+ AC_LIBOBJ(mmap)
+ AC_MSG_NOTICE([using custom mmap for Cygwin/MinGW])
+ ;;
+esac
+
+AC_CONFIG_FILES(src/win/Makefile src/Makefile Makefile)
+AC_OUTPUT
+
--- /dev/null
+all: css userguide pdf changelog perl
+
+userguide: txt/userguide.txt
+ asciidoc --unsafe --out-file=html/userguide.html -a linkcss=1 -a theme=tdh txt/userguide.txt
+
+changelog: txt/ChangeLog.txt
+ asciidoc --out-file=html/ChangeLog.html txt/ChangeLog.txt
+
+.PHONY: pdf
+
+pdf: txt/userguide.txt
+ a2x -f pdf $<
+ mv txt/userguide.pdf pdf/
+ cd html && ln -sf ../pdf/userguide.pdf userguide.pdf
+ rm txt/userguide.xml
+
+perl: txt/perl.txt
+ asciidoc --unsafe --out-file=html/perl.html -a linkcss=1 -a theme=tdh txt/perl.txt
+
+css: html/toc.css
+ cat /etc/asciidoc/stylesheets/xhtml11.css html/toc.css > html/tdh.css
+ cp /etc/asciidoc/stylesheets/xhtml11-quirks.css html/tdh-quirks.css
+
+docbook: txt/userguide.txt
+ asciidoc -b docbook --out-file=/tmp/userguide.xml txt/userguide.txt
+ xmlto -o html html-nochunks /tmp/userguide.xml
--- /dev/null
+# maintainer notes
+
+# IE6 png gamma bug:
+# PNG images in IE6 display with wrong background colors,
+# solution: save PNG in Gimp *Without save gamma checked*
+
+#update sourceforge web site:
+#cd html
+#scp *.{html,css} thanson@shell.sourceforge.net:/home/groups/t/tp/tpl/htdocs
+#cd img
+#scp *.png *.jpg thanson@shell.sourceforge.net:/home/groups/t/tp/tpl/htdocs/img
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
+<meta name="generator" content="AsciiDoc 8.5.0" />\r
+<title>tpl ChangeLog</title>\r
+<style type="text/css">\r
+/* Debug borders */\r
+p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {\r
+/*\r
+ border: 1px solid red;\r
+*/\r
+}\r
+\r
+body {\r
+ margin: 1em 5% 1em 5%;\r
+}\r
+\r
+a {\r
+ color: blue;\r
+ text-decoration: underline;\r
+}\r
+a:visited {\r
+ color: fuchsia;\r
+}\r
+\r
+em {\r
+ font-style: italic;\r
+ color: navy;\r
+}\r
+\r
+strong {\r
+ font-weight: bold;\r
+ color: #083194;\r
+}\r
+\r
+tt {\r
+ color: navy;\r
+}\r
+\r
+h1, h2, h3, h4, h5, h6 {\r
+ color: #527bbd;\r
+ font-family: sans-serif;\r
+ margin-top: 1.2em;\r
+ margin-bottom: 0.5em;\r
+ line-height: 1.3;\r
+}\r
+\r
+h1, h2, h3 {\r
+ border-bottom: 2px solid silver;\r
+}\r
+h2 {\r
+ padding-top: 0.5em;\r
+}\r
+h3 {\r
+ float: left;\r
+}\r
+h3 + * {\r
+ clear: left;\r
+}\r
+\r
+div.sectionbody {\r
+ font-family: serif;\r
+ margin-left: 0;\r
+}\r
+\r
+hr {\r
+ border: 1px solid silver;\r
+}\r
+\r
+p {\r
+ margin-top: 0.5em;\r
+ margin-bottom: 0.5em;\r
+}\r
+\r
+ul, ol, li > p {\r
+ margin-top: 0;\r
+}\r
+\r
+pre {\r
+ padding: 0;\r
+ margin: 0;\r
+}\r
+\r
+span#author {\r
+ color: #527bbd;\r
+ font-family: sans-serif;\r
+ font-weight: bold;\r
+ font-size: 1.1em;\r
+}\r
+span#email {\r
+}\r
+span#revnumber, span#revdate, span#revremark {\r
+ font-family: sans-serif;\r
+}\r
+\r
+div#footer {\r
+ font-family: sans-serif;\r
+ font-size: small;\r
+ border-top: 2px solid silver;\r
+ padding-top: 0.5em;\r
+ margin-top: 4.0em;\r
+}\r
+div#footer-text {\r
+ float: left;\r
+ padding-bottom: 0.5em;\r
+}\r
+div#footer-badges {\r
+ float: right;\r
+ padding-bottom: 0.5em;\r
+}\r
+\r
+div#preamble {\r
+ margin-top: 1.5em;\r
+ margin-bottom: 1.5em;\r
+}\r
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,\r
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
+div.admonitionblock {\r
+ margin-top: 0.25em;\r
+ margin-bottom: 1.5em;\r
+}\r
+div.admonitionblock {\r
+ margin-top: 2.5em;\r
+ margin-bottom: 2.5em;\r
+}\r
+\r
+div.content { /* Block element content. */\r
+ padding: 0;\r
+}\r
+\r
+/* Block element titles. */\r
+div.title, caption.title {\r
+ color: #527bbd;\r
+ font-family: sans-serif;\r
+ font-weight: bold;\r
+ text-align: left;\r
+ margin-top: 1.0em;\r
+ margin-bottom: 0.5em;\r
+}\r
+div.title + * {\r
+ margin-top: 0;\r
+}\r
+\r
+td div.title:first-child {\r
+ margin-top: 0.0em;\r
+}\r
+div.content div.title:first-child {\r
+ margin-top: 0.0em;\r
+}\r
+div.content + div.title {\r
+ margin-top: 0.0em;\r
+}\r
+\r
+div.sidebarblock > div.content {\r
+ background: #ffffee;\r
+ border: 1px solid silver;\r
+ padding: 0.5em;\r
+}\r
+\r
+div.listingblock > div.content {\r
+ border: 1px solid silver;\r
+ background: #f4f4f4;\r
+ padding: 0.5em;\r
+}\r
+\r
+div.quoteblock {\r
+ padding-left: 2.0em;\r
+ margin-right: 10%;\r
+}\r
+div.quoteblock > div.attribution {\r
+ padding-top: 0.5em;\r
+ text-align: right;\r
+}\r
+\r
+div.verseblock {\r
+ padding-left: 2.0em;\r
+ margin-right: 10%;\r
+}\r
+div.verseblock > div.content {\r
+ white-space: pre;\r
+}\r
+div.verseblock > div.attribution {\r
+ padding-top: 0.75em;\r
+ text-align: left;\r
+}\r
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
+div.verseblock + div.attribution {\r
+ text-align: left;\r
+}\r
+\r
+div.admonitionblock .icon {\r
+ vertical-align: top;\r
+ font-size: 1.1em;\r
+ font-weight: bold;\r
+ text-decoration: underline;\r
+ color: #527bbd;\r
+ padding-right: 0.5em;\r
+}\r
+div.admonitionblock td.content {\r
+ padding-left: 0.5em;\r
+ border-left: 2px solid silver;\r
+}\r
+\r
+div.exampleblock > div.content {\r
+ border-left: 2px solid silver;\r
+ padding: 0.5em;\r
+}\r
+\r
+div.imageblock div.content { padding-left: 0; }\r
+span.image img { border-style: none; }\r
+a.image:visited { color: white; }\r
+\r
+dl {\r
+ margin-top: 0.8em;\r
+ margin-bottom: 0.8em;\r
+}\r
+dt {\r
+ margin-top: 0.5em;\r
+ margin-bottom: 0;\r
+ font-style: normal;\r
+ color: navy;\r
+}\r
+dd > *:first-child {\r
+ margin-top: 0.1em;\r
+}\r
+\r
+ul, ol {\r
+ list-style-position: outside;\r
+}\r
+ol.arabic {\r
+ list-style-type: decimal;\r
+}\r
+ol.loweralpha {\r
+ list-style-type: lower-alpha;\r
+}\r
+ol.upperalpha {\r
+ list-style-type: upper-alpha;\r
+}\r
+ol.lowerroman {\r
+ list-style-type: lower-roman;\r
+}\r
+ol.upperroman {\r
+ list-style-type: upper-roman;\r
+}\r
+\r
+div.compact ul, div.compact ol,\r
+div.compact p, div.compact p,\r
+div.compact div, div.compact div {\r
+ margin-top: 0.1em;\r
+ margin-bottom: 0.1em;\r
+}\r
+\r
+div.tableblock > table {\r
+ border: 3px solid #527bbd;\r
+}\r
+thead {\r
+ font-family: sans-serif;\r
+ font-weight: bold;\r
+}\r
+tfoot {\r
+ font-weight: bold;\r
+}\r
+td > div.verse {\r
+ white-space: pre;\r
+}\r
+p.table {\r
+ margin-top: 0;\r
+}\r
+/* Because the table frame attribute is overriden by CSS in most browsers. */\r
+div.tableblock > table[frame="void"] {\r
+ border-style: none;\r
+}\r
+div.tableblock > table[frame="hsides"] {\r
+ border-left-style: none;\r
+ border-right-style: none;\r
+}\r
+div.tableblock > table[frame="vsides"] {\r
+ border-top-style: none;\r
+ border-bottom-style: none;\r
+}\r
+\r
+\r
+div.hdlist {\r
+ margin-top: 0.8em;\r
+ margin-bottom: 0.8em;\r
+}\r
+div.hdlist tr {\r
+ padding-bottom: 15px;\r
+}\r
+dt.hdlist1.strong, td.hdlist1.strong {\r
+ font-weight: bold;\r
+}\r
+td.hdlist1 {\r
+ vertical-align: top;\r
+ font-style: normal;\r
+ padding-right: 0.8em;\r
+ color: navy;\r
+}\r
+td.hdlist2 {\r
+ vertical-align: top;\r
+}\r
+div.hdlist.compact tr {\r
+ margin: 0;\r
+ padding-bottom: 0;\r
+}\r
+\r
+.comment {\r
+ background: yellow;\r
+}\r
+\r
+@media print {\r
+ div#footer-badges { display: none; }\r
+}\r
+\r
+div#toctitle {\r
+ color: #527bbd;\r
+ font-family: sans-serif;\r
+ font-size: 1.1em;\r
+ font-weight: bold;\r
+ margin-top: 1.0em;\r
+ margin-bottom: 0.1em;\r
+}\r
+\r
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
+ margin-top: 0;\r
+ margin-bottom: 0;\r
+}\r
+div.toclevel2 {\r
+ margin-left: 2em;\r
+ font-size: 0.9em;\r
+}\r
+div.toclevel3 {\r
+ margin-left: 4em;\r
+ font-size: 0.9em;\r
+}\r
+div.toclevel4 {\r
+ margin-left: 6em;\r
+ font-size: 0.9em;\r
+}\r
+/* Workarounds for IE6's broken and incomplete CSS2. */\r
+\r
+div.sidebar-content {\r
+ background: #ffffee;\r
+ border: 1px solid silver;\r
+ padding: 0.5em;\r
+}\r
+div.sidebar-title, div.image-title {\r
+ color: #527bbd;\r
+ font-family: sans-serif;\r
+ font-weight: bold;\r
+ margin-top: 0.0em;\r
+ margin-bottom: 0.5em;\r
+}\r
+\r
+div.listingblock div.content {\r
+ border: 1px solid silver;\r
+ background: #f4f4f4;\r
+ padding: 0.5em;\r
+}\r
+\r
+div.quoteblock-attribution {\r
+ padding-top: 0.5em;\r
+ text-align: right;\r
+}\r
+\r
+div.verseblock-content {\r
+ white-space: pre;\r
+}\r
+div.verseblock-attribution {\r
+ padding-top: 0.75em;\r
+ text-align: left;\r
+}\r
+\r
+div.exampleblock-content {\r
+ border-left: 2px solid silver;\r
+ padding-left: 0.5em;\r
+}\r
+\r
+/* IE6 sets dynamically generated links as visited. */\r
+div#toc a:visited { color: blue; }\r
+</style>\r
+</head>\r
+<body>\r
+<div id="header">\r
+<h1>tpl ChangeLog</h1>\r
+</div>\r
+<h2 id="_version_1_5_2010_02_05">Version 1.5 (2010-02-05)</h2>\r
+<div class="sectionbody">\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+tpl now builds as a DLL under Microsoft Visual Studio! (thanks, degski and Zhang Yafei!)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+there are now two download options: the <a href="http://downloads.sourceforge.net/tpl/libtpl-1.5.tar.bz2">tarball</a> and the Visual Studio <a href="http://downloads.sourceforge.net/tpl/tpl-1.5-vs2008.zip">solution</a>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+a crash in <tt>tpl_free</tt> on certain format strings has been fixed (thanks, Eric Rose!)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+fixed a bug in <tt>tpl_dump</tt> on 64-bit, big-endian platforms\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+changed some pointer casts from <tt>long</tt> to <tt>uintptr_t</tt> since 64-bit Windows has 32-bit longs\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+tpl has been downloaded 4,195 times.\r
+</p>\r
+</li>\r
+</ul></div>\r
+</div>\r
+<h2 id="_version_1_4_2009_04_21">Version 1.4 (2009-04-21)</h2>\r
+<div class="sectionbody">\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+fixed-length arrays can now be multi-dimensional like <tt>i##</tt>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+fixed-length string arrays like <tt>s#</tt> are now supported\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+nested structures can now be expressed, using the dollar symbol, e.g. <tt>S(ci$(cc))</tt>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+<tt>tpl_dump</tt> can use a caller-allocated output buffer (<tt>TPL_MEM|TPL_PREALLOCD</tt>)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+<tt>tpl_load</tt> can tolerate excess space in input buffer (<tt>TPL_MEM|TPL_EXCESS_OK</tt>)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+implement <tt>TPL_FXLENS</tt> flag for <tt>tpl_peek</tt> to get lengths of fixed-length arrays\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+implement <tt>TPL_GETSIZE</tt> flag for <tt>tpl_dump</tt> to get dump size without dumping\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+fix success return code from <tt>tpl_dump(TPL_FD,...)</tt> (thanks, Max Lapan!)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+deprecated the wildcard unpacking <tt>S(*)</tt> feature\r
+</p>\r
+</li>\r
+</ul></div>\r
+</div>\r
+<h2 id="_version_1_3_2009_02_10">Version 1.3 (2009-02-10)</h2>\r
+<div class="sectionbody">\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+added <tt>TPL_DATAPEEK</tt> mode for <tt>tpl_peek</tt>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added support for <tt>NULL</tt> strings\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added support for 16-bit integer types (<tt>j</tt>,<tt>v</tt>)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added <tt>tpl_jot</tt>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added support for fixed-length arrays of structures <tt>S(...)#</tt>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added support for pre-C99 compilers (thanks, Wei Wei!)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+improved structure alignment calculation (thanks, Wu Yongwei!)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added RPM spec file (thanks, Alessandro Ren!)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+compiles cleanly with <tt>-Wall</tt> and <tt>-pedantic</tt> and with <tt>-O3</tt>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+made <a href="license.html">BSD license</a> terms even more permissive\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+test suite: exit with status zero when all tests pass\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added PDF user guide\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added <a href="http://troydhanson.wordpress.com/feed/">update news</a> <span class="image">\r
+<img src="img/rss.png" alt="(RSS)" />\r
+</span>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+added <a href="http://apps.sourceforge.net/mediawiki/tpl/">tpl wiki</a>\r
+</p>\r
+</li>\r
+</ul></div>\r
+</div>\r
+<h2 id="_version_1_2_2007_04_27">Version 1.2 (2007-04-27)</h2>\r
+<div class="sectionbody">\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+Perl API and XML converter support 64-bit types\r
+</p>\r
+</li>\r
+</ul></div>\r
+</div>\r
+<h2 id="_version_1_1_2007_04_25">Version 1.1 (2007-04-25)</h2>\r
+<div class="sectionbody">\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+support for serializing C structures\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+support for serializing fixed-length arrays\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+MinGW support (thanks, Horea Haitonic!)\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+revised User Guide\r
+</p>\r
+</li>\r
+</ul></div>\r
+</div>\r
+<h2 id="_version_1_0_2006_09_28">Version 1.0 (2006-09-28)</h2>\r
+<div class="sectionbody">\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+Initial version\r
+</p>\r
+</li>\r
+</ul></div>\r
+</div>\r
+<div id="footer">\r
+<div id="footer-text">\r
+Last updated 2010-02-05 19:31:37 PDT\r
+</div>\r
+</div>\r
+</body>\r
+</html>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="728px"
+ height="90px"
+ id="svg1323"
+ sodipodi:version="0.32"
+ inkscape:version="0.44"
+ sodipodi:docbase="/home/thanson/code/tpl/trunk/doc/html/img"
+ sodipodi:docname="banner.svg"
+ inkscape:export-filename="/home/thanson/code/tpl/trunk/doc/html/img/banner.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs1325">
+ <linearGradient
+ id="linearGradient22211">
+ <stop
+ style="stop-color:#2e7ce0;stop-opacity:0;"
+ offset="0"
+ id="stop22237" />
+ <stop
+ id="stop22215"
+ offset="1"
+ style="stop-color:#2e7ce0;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient20106">
+ <stop
+ style="stop-color:#f0f3f7;stop-opacity:1;"
+ offset="0"
+ id="stop20108" />
+ <stop
+ style="stop-color:#f0f3f7;stop-opacity:0;"
+ offset="1"
+ id="stop20110" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19154">
+ <stop
+ style="stop-color:#005fd9;stop-opacity:1;"
+ offset="0"
+ id="stop19156" />
+ <stop
+ style="stop-color:#005fd9;stop-opacity:0;"
+ offset="1"
+ id="stop19158" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient19142">
+ <stop
+ style="stop-color:#4ec2f0;stop-opacity:1;"
+ offset="0"
+ id="stop19144" />
+ <stop
+ style="stop-color:#4ec2f0;stop-opacity:0;"
+ offset="1"
+ id="stop19146" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19132">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop19134" />
+ <stop
+ id="stop19140"
+ offset="0.5"
+ style="stop-color:#00d1f6;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop19136" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19142"
+ id="radialGradient19150"
+ cx="651"
+ cy="50"
+ fx="651"
+ fy="50"
+ r="49.5"
+ gradientTransform="matrix(1,0,0,0.616162,1.124101e-15,19.19192)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient19160"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient19224"
+ gradientUnits="userSpaceOnUse"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47"
+ gradientTransform="matrix(5.590915,0,0,1.101015,-3310.4,161.4223)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient20106"
+ id="radialGradient20112"
+ cx="125.1579"
+ cy="213.03658"
+ fx="125.1579"
+ fy="213.03658"
+ r="27.577164"
+ gradientTransform="matrix(1,0,0,0.923077,0,16.38742)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient20176"
+ gradientUnits="userSpaceOnUse"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47"
+ gradientTransform="translate(-38.18377,318.1981)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient20203"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-38.18377,318.1981)"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient20269"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-38.18377,318.1981)"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient20389"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.755096,0,0,1.101015,-1053.937,335.4223)"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient22243"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.867037,0,0,1.101015,-1120.758,425.4223)"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient24137"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.755096,0,0,1.101015,-1052.937,435.4223)"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient19154"
+ id="linearGradient24144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.755096,0,0,1.101015,-1049.937,270.4223)"
+ x1="588"
+ y1="47"
+ x2="722"
+ y2="47" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="497.48754"
+ inkscape:cy="-4.5816549"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1176"
+ inkscape:window-height="633"
+ inkscape:window-x="23"
+ inkscape:window-y="48" />
+ <metadata
+ id="metadata1328">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <rect
+ style="fill:#3c85e2;fill-opacity:1;fill-rule:nonzero;stroke:#ffcb53;stroke-width:4.0415926;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect20271"
+ width="240.0993"
+ height="77.071068"
+ x="4.1015167"
+ y="3.6345825"
+ rx="29.60623" />
+ <rect
+ y="46.014687"
+ x="-236.92256"
+ height="16.982321"
+ width="16.982321"
+ id="rect24091"
+ style="fill:#2fa4db;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ transform="scale(-1,1)" />
+ <rect
+ y="46.014687"
+ x="-219.80927"
+ height="16.982321"
+ width="16.982321"
+ id="rect24093"
+ style="fill:#2fa4db;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ transform="scale(-1,1)" />
+ <rect
+ style="fill:#2fa4db;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect24095"
+ width="16.982321"
+ height="16.982321"
+ x="-202.92258"
+ y="46.014687"
+ transform="scale(-1,1)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#e33019;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24097"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ transform="translate(-102.0443,123.8305)" />
+ <path
+ transform="translate(-76.04438,87.8305)"
+ sodipodi:type="arc"
+ style="fill:#e38c19;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24099"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z" />
+ <path
+ transform="translate(-64.0444,105.8305)"
+ sodipodi:type="arc"
+ style="fill:#88dfdb;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24101"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24103"
+ style="fill:#79c71a;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="translate(-76.04438,123.8305)" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24105"
+ style="fill:#f5e1a2;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="translate(-88.04433,105.8305)" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24107"
+ style="fill:#d8643e;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(-1,0,0,1,460.1983,124.4007)" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24109"
+ style="fill:#d8643e;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(-1,0,0,1,442.9488,124.4007)" />
+ <path
+ transform="matrix(-1,0,0,1,426.1983,124.4007)"
+ sodipodi:type="arc"
+ style="fill:#d8643e;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24111"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 146.68046,40.305464 L 153.06062,49.875694"
+ id="path24113"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 146.68046,31.875694 L 153.06062,22.305464"
+ id="path24115"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;marker-end:none;stroke-opacity:1"
+ d="M 158.68046,22.305464 L 165.06062,31.875694"
+ id="path24117"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 224.06859,54.660809 L 215.24887,54.660809"
+ id="path24119"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 206.81911,54.660809 L 198.49837,54.660809"
+ id="path24121"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 140.5923,40.305464 L 133.14878,49.875694"
+ id="path24123"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 198.08195,67.740894 L 198.08195,67.740894"
+ id="path24125"
+ inkscape:connector-type="polyline" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24127"
+ style="fill:#d8643e;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(-1,0,0,1,410.1983,124.4007)" />
+ <path
+ inkscape:connector-type="polyline"
+ id="path24129"
+ d="M 190.81911,54.660809 L 182.49837,54.660809"
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 170.23387,40.305398 L 175.92003,50.445833"
+ id="path24131"
+ inkscape:connector-type="polyline" />
+ <text
+ xml:space="preserve"
+ style="font-size:57.24448395px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff8900;fill-opacity:1;stroke:#ffc900;stroke-width:2.58558559px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.95530725;font-family:Bitstream Vera Sans Mono"
+ x="13.482153"
+ y="61.413116"
+ id="text24133"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan24135"
+ x="13.482153"
+ y="61.413116"
+ style="font-size:57.24448395px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff8900;fill-opacity:1;stroke:#ffc900;stroke-opacity:0.95530725;font-family:Bitstream Vera Sans Mono">tpl</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:33.35416794px;font-style:normal;font-weight:normal;fill:#ff8900;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="313.95825"
+ y="31.16275"
+ id="text24140"><tspan
+ sodipodi:role="line"
+ id="tspan24142"
+ x="313.95825"
+ y="31.16275"
+ style="stroke:none;stroke-opacity:1">easily store and retrieve </tspan><tspan
+ sodipodi:role="line"
+ x="313.95825"
+ y="72.85546"
+ style="font-weight:bold;stroke:none;stroke-opacity:1"
+ id="tspan1941">binary data in C</tspan><tspan
+ sodipodi:role="line"
+ x="313.95825"
+ y="114.54817"
+ style="stroke:none;stroke-opacity:1"
+ id="tspan1939" /></text>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.43"
+ sodipodi:docbase="/home/thanson/code/tpl/doc/html/img"
+ sodipodi:docname="grad_cyan.svg">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient2194">
+ <stop
+ style="stop-color:#1190ed;stop-opacity:1;"
+ offset="0"
+ id="stop2196" />
+ <stop
+ style="stop-color:#f9f9f9;stop-opacity:0;"
+ offset="1"
+ id="stop2198" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2184">
+ <stop
+ style="stop-color:#0000e0;stop-opacity:1;"
+ offset="0"
+ id="stop2186" />
+ <stop
+ style="stop-color:#0000e0;stop-opacity:0;"
+ offset="1"
+ id="stop2188" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2184"
+ id="linearGradient2190"
+ x1="76.642857"
+ y1="679.50504"
+ x2="523.35714"
+ y2="679.50504"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2194"
+ id="linearGradient2200"
+ x1="335.5"
+ y1="654.61218"
+ x2="506"
+ y2="654.61218"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="488.2215"
+ inkscape:cy="372.85714"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="797"
+ inkscape:window-height="575"
+ inkscape:window-x="112"
+ inkscape:window-y="25" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ style="opacity:0.96629214;fill:url(#linearGradient2200);fill-opacity:1.0;stroke:none;stroke-width:4.68499994;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2192"
+ width="170.5"
+ height="9.5"
+ x="335.5"
+ y="649.86218"
+ inkscape:export-filename="/home/thanson/code/tpl/doc/html/img/grad_cyan.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="105.05521"
+ height="34.90324"
+ id="svg2267"
+ sodipodi:version="0.32"
+ inkscape:version="0.44"
+ version="1.0"
+ sodipodi:docbase="/home/thanson/code/uthash/trunk/doc/html/img"
+ sodipodi:docname="tpl-mini.svg">
+ <defs
+ id="defs3" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="63.027613"
+ inkscape:cy="11.686998"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ width="118.44px"
+ height="22.66px"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="916"
+ inkscape:window-height="626"
+ inkscape:window-x="15"
+ inkscape:window-y="95">
+ <sodipodi:guide
+ orientation="horizontal"
+ position="28.768574"
+ id="guide2299" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2271">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-69.67658,-43.42744)">
+ <rect
+ style="fill:#3c85e2;fill-opacity:1;fill-rule:nonzero;stroke:#ffcb53;stroke-width:1.73912036;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect20271"
+ width="103.31609"
+ height="33.16412"
+ x="70.546143"
+ y="44.296997"
+ rx="12.739729" />
+ <rect
+ y="62.533398"
+ x="-170.73035"
+ height="7.3075895"
+ width="7.3075895"
+ id="rect24091"
+ style="fill:#2fa4db;fill-opacity:1;stroke:black;stroke-width:0.43030569;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ transform="scale(-1,1)" />
+ <rect
+ y="62.533398"
+ x="-163.36638"
+ height="7.3075895"
+ width="7.3075895"
+ id="rect24093"
+ style="fill:#2fa4db;fill-opacity:1;stroke:black;stroke-width:0.43030569;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ transform="scale(-1,1)" />
+ <rect
+ style="fill:#2fa4db;fill-opacity:1;stroke:black;stroke-width:0.43030569;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect24095"
+ width="7.3075895"
+ height="7.3075895"
+ x="-156.09996"
+ y="62.533398"
+ transform="scale(-1,1)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#e33019;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24097"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ transform="matrix(0.430306,0,0,0.430306,24.87099,96.01799)" />
+ <path
+ transform="matrix(0.430306,0,0,0.430306,36.05889,80.52698)"
+ sodipodi:type="arc"
+ style="fill:#e38c19;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24099"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z" />
+ <path
+ transform="matrix(0.430306,0,0,0.430306,41.22256,88.27247)"
+ sodipodi:type="arc"
+ style="fill:#88dfdb;fill-opacity:1;fill-rule:nonzero;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24101"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24103"
+ style="fill:#79c71a;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(0.430306,0,0,0.430306,36.05889,96.01799)" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24105"
+ style="fill:#f5e1a2;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(0.430306,0,0,0.430306,30.89524,88.27247)" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24107"
+ style="fill:#d8643e;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(-0.430306,0,0,0.430306,266.8073,96.26334)" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24109"
+ style="fill:#d8643e;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(-0.430306,0,0,0.430306,259.3847,96.26334)" />
+ <path
+ transform="matrix(-0.430306,0,0,0.430306,252.1769,96.26334)"
+ sodipodi:type="arc"
+ style="fill:#d8643e;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path24111"
+ sodipodi:cx="231.91481"
+ sodipodi:cy="-69.739944"
+ sodipodi:rx="3.7148824"
+ sodipodi:ry="3.7148824"
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 131.89866,60.076683 L 134.64408,64.194808"
+ id="path24113"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 131.89866,56.449305 L 134.64408,52.33118"
+ id="path24115"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:square;stroke-linejoin:miter;marker-end:none;stroke-opacity:1"
+ d="M 137.06234,52.33118 L 139.80775,56.449305"
+ id="path24117"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 165.19922,66.25387 L 161.40404,66.25387"
+ id="path24119"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 157.77667,66.25387 L 154.19622,66.25387"
+ id="path24121"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 129.2789,60.076683 L 126.07591,64.194808"
+ id="path24123"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 154.01702,71.882304 L 154.01702,71.882304"
+ id="path24125"
+ inkscape:connector-type="polyline" />
+ <path
+ d="M 235.62969 -69.739944 A 3.7148824 3.7148824 0 1 1 228.19993,-69.739944 A 3.7148824 3.7148824 0 1 1 235.62969 -69.739944 z"
+ sodipodi:ry="3.7148824"
+ sodipodi:rx="3.7148824"
+ sodipodi:cy="-69.739944"
+ sodipodi:cx="231.91481"
+ id="path24127"
+ style="fill:#d8643e;fill-opacity:1;stroke:black;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(-0.430306,0,0,0.430306,245.292,96.26334)" />
+ <path
+ inkscape:connector-type="polyline"
+ id="path24129"
+ d="M 150.89178,66.25387 L 147.31131,66.25387"
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:0.43030569px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 142.03383,60.076654 L 144.48062,64.440141"
+ id="path24131"
+ inkscape:connector-type="polyline" />
+ <text
+ xml:space="preserve"
+ style="font-size:24.63262939px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff8900;fill-opacity:1;stroke:#ffc900;stroke-width:1.11259222px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.95530725;font-family:Bitstream Vera Sans Mono"
+ x="74.58268"
+ y="69.159424"
+ id="text24133"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan24135"
+ x="74.58268"
+ y="69.159424"
+ style="font-size:24.63262939px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ff8900;fill-opacity:1;stroke:#ffc900;stroke-width:1.11259222;stroke-opacity:0.95530725;font-family:Bitstream Vera Sans Mono">tpl</tspan></text>
+ </g>
+</svg>
--- /dev/null
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XTHML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <link rel="stylesheet" type="text/css" href="styles.css" />
+ <title>tpl home page</title>
+ </head>
+ <body>
+
+ <div id="banner">
+ <img src="img/banner.png" alt="easy data storage and retrieval in C" />
+ </div> <!-- banner -->
+
+ <div id="topnav">
+ <a href="http://sourceforge.net/projects/tpl/">sf.net summary page</a> >
+ tpl home
+ </div> <!-- topnav -->
+
+ <hr />
+ <div id="mid">
+
+ <div id="nav">
+
+
+ <h2>documentation</h2>
+ <div><a href="userguide.html">user guide</a> (<a href="userguide.html">html</a>, <a href="userguide.pdf">pdf</a>)</div>
+
+ <h2>download</h2>
+ <h3>Linux, Mac OSX, Solaris, BSD</h3>
+ <div><a href="http://downloads.sourceforge.net/tpl/libtpl-1.5.tar.bz2">libtpl-1.5.tar.bz2</a></div>
+ <h3>Visual Studio 2008 Solution</h3>
+ <div><a href="http://downloads.sourceforge.net/tpl/tpl-1.5-vs2008.zip">tpl-1.5-vs2008.zip</a></div>
+
+ <h2>last release</h2>
+ <div>February, 2010</div>
+ <div><a href="ChangeLog.html">ChangeLog</a></div>
+
+ <h2>license</h2>
+ <div><a href="license.html">BSD revised</a></div>
+
+ <h2>news feed</h2>
+ <div><a href="http://troydhanson.wordpress.com/">updates blog</a> (<a href="http://troydhanson.wordpress.com/feed/">rss</a>)<img alt=" rss" src="img/rss.png"/></div>
+
+ <h2>platforms</h2>
+ <div>linux</div>
+ <div>os x</div>
+ <div>windows</div>
+ <div>solaris</div>
+ <div>openbsd</div>
+
+ <h2>other projects</h2>
+ <div><a href="http://uthash.sourceforge.net/">uthash</a></div>
+ <div><a href="http://tkhanson.net/misc/">scripts & snippets</a></div>
+
+ <h2>developer</h2>
+ <div>Troy D. Hanson</div>
+ <div>tdh at tkhanson.net</div>
+
+ </div> <!-- nav -->
+
+ <div id="main">
+
+<div>
+<div class="lead">Efficient serialization in C</div>
+You can use tpl to store and reload your C data quickly and easily.
+Tpl works with files, memory buffers and file descriptors so it's
+suitable for use as a file format, IPC message format or any scenario
+where you need to store and retrieve your data.
+</div>
+
+<div>
+<div class="lead">Express your data</div>
+Just express the type of data you are working with as a tpl format string. For
+example, if you have a list of numeric ids and corresponding usernames, your
+format string is <em>A(is)</em>. Map your C variables to the format string and
+then pack or unpack data. The format string lets you focus on your data,
+rather than the storage format.
+</div>
+
+<div class="listing">
+<table summary="example of storing and reloading an integer array">
+<tr>
+<th>
+Storing ids and usernames
+</th>
+<th>
+Reloading ids and usernames
+</th>
+</tr>
+<tr>
+<td>
+<div class="code">
+<pre>
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ int id=0;
+ char *name, *names[] = { "joe", "bob", "cary" };
+
+ tn = tpl_map("A(is)", &id, &name);
+
+ for(name=names[0]; id < 3; name=names[++id]) {
+ tpl_pack(tn,1);
+ }
+
+ tpl_dump(tn, TPL_FILE, "users.tpl");
+ tpl_free(tn);
+}
+</pre>
+</div> <!-- code -->
+</td>
+<td>
+<div class="code">
+<pre>
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ int id;
+ char *name;
+
+ tn = tpl_map("A(is)", &id, &name);
+ tpl_load(tn, TPL_FILE, "users.tpl");
+
+ while ( tpl_unpack(tn,1) > 0 ) {
+ printf("id %d, user %s\n", id, name);
+ free(name);
+ }
+ tpl_free(tn);
+}
+</pre>
+</div> <!-- code -->
+</td>
+</tr>
+</table>
+</div> <!-- listing -->
+
+<div>
+<div class="lead">No library dependencies</div>
+Tpl does not make your software dependent on any libraries. You can compile its
+source code (one file) right into your program.
+</div>
+
+<div class="lead">For more information</div>
+For a more thorough explanation and more examples, please read the
+<a href="userguide.html">User Guide.</a>
+
+</div> <!-- main -->
+</div> <!-- mid -->
+
+ <hr />
+ <div id="footer">
+ <a href="http://sourceforge.net/projects/tpl"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=157637&type=13" width="120" height="30" alt="SourceForge.net." /></a>
+ <p>This project is hosted on SourceForge.net</p>
+ <p>$Id: index.html 192 2009-04-24 10:35:30Z thanson $</p>
+ </div> <!-- footer -->
+
+ </body>
+
+</html>
--- /dev/null
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XTHML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <link rel="stylesheet" type="text/css" href="styles.css" />
+ <title>tpl home page</title>
+ </head>
+ <body>
+
+ <div id="banner">
+ <img src="img/banner.png" alt="serialization in C: easy data storage and retrieval" />
+ </div> <!-- banner -->
+
+ <div id="topnav">
+ <a href="http://sourceforge.net/projects/tpl/">sf.net summary page</a> >
+ <a href="index.html">tpl home</a> >
+ BSD license
+ </div> <!-- topnav -->
+
+ <hr />
+ <div id="mid">
+
+
+ <div id="main">
+
+<pre>
+Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+</pre>
+</div> <!-- main -->
+</div> <!-- mid -->
+
+ <hr />
+ <div id="footer">
+ <a href="http://sourceforge.net/projects/tpl"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=157637&type=13" width="120" height="30" alt="SourceForge.net." /></a>
+ <p>This project is hosted on SourceForge.net</p>
+ <p>$Id: index.html 124 2007-04-27 06:41:36Z thanson $</p>
+ </div> <!-- footer -->
+
+ </body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
+<meta name="generator" content="AsciiDoc 8.3.4" />\r
+<title>tpl Perl API</title>\r
+<link rel="stylesheet" href="./tdh.css" type="text/css" />\r
+<link rel="stylesheet" href="./tdh-quirks.css" type="text/css" />\r
+</head>\r
+<body>\r
+<div id="header">\r
+<h1>tpl Perl API</h1>\r
+<span id="author">Troy D. Hanson</span><br />\r
+<span id="email"><tt><<a href="mailto:troydhanson@comcast.net">troydhanson@comcast.net</a>></tt></span><br />\r
+<span id="revision">version 1.1,</span>\r
+April 2007\r
+</div>\r
+<div id="preamble">\r
+<div class="sectionbody">\r
+<a style="float: right;" href="http://sourceforge.net/projects/tpl"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=157637&type=16" width="150" height="40" alt="SourceForge.net" /></a>\r
+ <div id="topnav" style="font-size: 9pt; font-family: sans-serif;">\r
+ <a style="padding: 8px;" href="http://sourceforge.net/projects/tpl/">sf.net summary page</a> >\r
+ <a style="padding: 8px;" href="index.html">tpl home</a> >\r
+ tpl Perl API\r
+ <a style="padding: 8px;" href="userguide.pdf">[View PDF]</a>\r
+ </div>\r
+</div>\r
+</div>\r
+<h2 id="_perl_api">Perl API</h2>\r
+<div class="sectionbody">\r
+<div id="toc"></div>\r
+<script>\r
+window.onload=generate_TOC\r
+\r
+/* Author: Mihai Bazon, September 2002\r
+ * <a href="http://students.infoiasi.ro/~mishoo">http://students.infoiasi.ro/~mishoo</a>\r
+ *\r
+ * Table Of Content generator\r
+ * Version: 0.4\r
+ *\r
+ * Feel free to use this script under the terms of the GNU General Public\r
+ * License, as long as you do not remove or alter this notice.\r
+ */\r
+\r
+ /* modified by Troy D. Hanson, September 2006. License: GPL */\r
+\r
+function H_getText(el) {\r
+ var text = "";\r
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
+ if (i.nodeType == 3 /* Node.TEXT_NODE, IE doesn't speak constants */)\r
+ text += i.data;\r
+ else if (i.firstChild != null)\r
+ text += H_getText(i);\r
+ }\r
+ return text;\r
+}\r
+\r
+function TOC_EL(el, text, level) {\r
+ this.element = el;\r
+ this.text = text;\r
+ this.level = level;\r
+}\r
+\r
+function getHeadlines(el) {\r
+ var l = new Array;\r
+ var rx = /[hH]([2-3])/;\r
+ // internal recursive function that scans the DOM tree\r
+ var rec = function (el) {\r
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
+ if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
+ if (rx.exec(i.tagName))\r
+ l[l.length] = new TOC_EL(i, H_getText(i), parseInt(RegExp.$1));\r
+ rec(i);\r
+ }\r
+ }\r
+ }\r
+ rec(el);\r
+ return l;\r
+}\r
+\r
+function generate_TOC() {\r
+ var parent = document.getElementById("toc");\r
+ var toc_hdr = document.createElement("div");\r
+ var toc_hdr_txt = document.createTextNode("CONTENTS");\r
+ toc_hdr.appendChild(toc_hdr_txt);\r
+ /* toc_hdr.setAttribute("id","hdr"); */\r
+ toc_hdr.id = "hdr";\r
+ parent.appendChild(toc_hdr);\r
+ var hs = getHeadlines(document.getElementsByTagName("body")[0]);\r
+ for (var i = 0; i < hs.length; ++i) {\r
+ var hi = hs[i];\r
+ var d = document.createElement("div");\r
+ if (hi.element.id == "") hi.element.id = "gen" + i;\r
+ var a = document.createElement("a");\r
+ a.href = "#" + hi.element.id;\r
+ a.appendChild(document.createTextNode(hi.text));\r
+ d.appendChild(a);\r
+ d.className = "level" + hi.level;\r
+ parent.appendChild(d);\r
+ /*\r
+ if (hi.level == 3) {\r
+ var dvtop = document.createElement("div");\r
+ dvtop.className = "toplink";\r
+ dvtop.appendChild(document.createTextNode("^top^"));\r
+ dvtop.onclick=function(){scrollTo(0,0);};\r
+ hi.element.appendChild(dvtop);\r
+ }\r
+ */\r
+ }\r
+}\r
+</script>\r
+<div class="paragraph"><p>The Perl API for reading and writing tpl is nearly identical to the C API. This\r
+document will briefly explain the Perl API and provide examples. The chief\r
+motivation for having a Perl API is to communicate with C programs that use tpl.</p></div>\r
+<div class="admonitionblock">\r
+<table><tr>\r
+<td class="icon">\r
+<div class="title">Tip</div>\r
+</td>\r
+<td class="content">\r
+<div class="title">Start with the C API</div>This document assumes familiarity with the C API. The concepts of using tpl\r
+are not explained here. For an introduction to tpl and its C API, see the\r
+<a href="userguide.html">User Guide</a>.</td>\r
+</tr></table>\r
+</div>\r
+<h3 id="_tpl_pm">Tpl.pm</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The <tt>Tpl.pm</tt> file (in the <tt>lang/perl</tt>) directory contains the Perl module. You\r
+can copy it to another directory if you wish. Your Perl program may need to\r
+include a <tt>use lib</tt> statement to find the module.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>#!/usr/bin/perl\r
+use lib "/some/directory";\r
+use Tpl;</tt></pre>\r
+</div></div>\r
+<h3 id="_tpl_map">tpl_map</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This function resembles the C version, except that it’s invoked via the <tt>Tpl</tt>\r
+module, and it takes references to Perl variables after the format string.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>my $i;\r
+my $tpl = Tpl->tpl_map("A(i)",\$i);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The return value is a tpl object; all other API calls are object methods.\r
+Incidentally, there is no <tt>tpl_free()</tt> method corresponding to the C API.</p></div>\r
+<h4 id="_fixed_length_arrays">Fixed-length arrays</h4>\r
+<div class="paragraph"><p>Format strings such as <tt>i#</tt> denote a fixed-length array. In the Perl API,\r
+fixed-length arrays require two arguments: a list reference, and the fixed\r
+length. For example:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>my @x;\r
+my $tpl = Tpl->tpl_map("i#", \@x, 10);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>When fixed-length arrays are packed or unpacked, the specified number of\r
+elements will be copied from (or placed into) the designated list.</p></div>\r
+<h4 id="_structures">Structures</h4>\r
+<div class="paragraph"><p>Format strings containing <tt>S(…)</tt> are handled in the Perl API as if only the\r
+interior, parenthesized part was present. (It does not work like the C API). So\r
+simply ignore the <tt>S(…)</tt> and consider only its interior format characters when\r
+constructing the argument list:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>my ($str, $int);\r
+my $tpl = Tpl->tpl_map("S(si)", \$str, \$int);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>It really only makes sense to use <tt>S(…)</tt> in a format string in the Perl API if\r
+you are communicating with a C program that uses structures.</p></div>\r
+<h3 id="_tpl_pack">tpl_pack</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This is nearly identical to the C version. The only argument is the index\r
+number to pack.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>$tpl->tpl_pack(1);</tt></pre>\r
+</div></div>\r
+<h3 id="_tpl_dump">tpl_dump</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This method is a little different than the C version. Given no arguments, it\r
+returns the tpl image; given one argument it writes a file with that name.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>$tpl->tpl_dump("demo.tpl"); # writes demo.tpl</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Or,</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>my $img = $tpl->tpl_dump();</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The tpl image is a binary buffer. You can do whatever you want with it, such as\r
+write it to a socket or pipe (probably to C program listening on the other end),\r
+or save it somewhere and later re-load it using <tt>tpl_load()</tt>.</p></div>\r
+<h3 id="_tpl_load">tpl_load</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This method loads a tpl image from a file or from a Perl variable. It takes\r
+one argument. If it’s not a reference, it’s assumed to be a filename to load.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>$tpl->tpl_load("demo.tpl");</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Otherwise, if the argument is a Perl reference, it’s construed as a variable\r
+containing the tpl image:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>$tpl->tpl_load(\$img);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The method will <tt>die</tt> if the image is invalid or the file doesn’t exist. You\r
+can wrap it with <tt>eval</tt> to catch such errors:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>eval { $tpl->tpl_load(\$img); };\r
+print "failed to load\n" if $@;</tt></pre>\r
+</div></div>\r
+<h3 id="_tpl_unpack">tpl_unpack</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This is nearly identical to the C version. The only argument is the index\r
+number to unpack.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>$tpl->tpl_unpack(1);</tt></pre>\r
+</div></div>\r
+</div>\r
+<h2 id="_examples">Examples</h2>\r
+<div class="sectionbody">\r
+<h3 id="_integer_array">Integer array</h3><div style="clear:left"></div>\r
+<div class="listingblock">\r
+<div class="title">Packing A(i) to file</div>\r
+<div class="content">\r
+<pre><tt>#!/usr/bin/perl\r
+\r
+use strict;\r
+use warnings;\r
+\r
+use Tpl;\r
+\r
+my $i;\r
+my $tpl = Tpl->tpl_map("A(i)",\$i);\r
+for($i=0; $i<10; $i++) {\r
+ $tpl->tpl_pack(1);\r
+}\r
+$tpl->tpl_dump("demo.tpl");</tt></pre>\r
+</div></div>\r
+<div class="listingblock">\r
+<div class="title">Unpacking A(i) from file</div>\r
+<div class="content">\r
+<pre><tt>#!/usr/bin/perl\r
+\r
+use strict;\r
+use warnings;\r
+\r
+use Tpl;\r
+\r
+my $j;\r
+my $tpl2 = Tpl->tpl_map("A(i)",\$j);\r
+$tpl2->tpl_load("demo.tpl");\r
+while($tpl2->tpl_unpack(1) > 0) {\r
+ print "$j\n";\r
+}</tt></pre>\r
+</div></div>\r
+<h3 id="_message_passing">Message-passing</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>While the bulk of this example is socket handling, it demonstrates how you can\r
+use tpl as a message-passing format. In the real-world, you might have a C\r
+server and a Perl client, for example. In this example, we’ll code both a client\r
+and a server in Perl.</p></div>\r
+<div class="sidebarblock">\r
+<div class="sidebar-content">\r
+<div class="sidebar-title">A server that sums integers</div>\r
+<div class="paragraph"><p>Programming literature is rife with contrived examples so we will follow in that\r
+tradition. Our server will do no more than sum a list of integers. But in doing\r
+so it will demonstrate message passing adequately. Both its input (the integer\r
+array) and its output (an integer) are tpl images, passed over a TCP/IP socket.</p></div>\r
+</div></div>\r
+<h4 id="_server">Server</h4>\r
+<div class="paragraph"><p>The server waits for a connection from a client. When it gets one, it accepts\r
+the connection and immediately forks a child process to handle it. Then it goes\r
+back to waiting for another new connection.</p></div>\r
+<div class="paragraph"><p>The server child process handles the client by loading and unpacking the tpl\r
+image sent by the client (containing an array of integers). It calculates their\r
+sum and constructs a new tpl image containing the sum, which it sends back to\r
+the client.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Server</div>\r
+<div class="content">\r
+<pre><tt>#!/usr/bin/perl\r
+\r
+use strict;\r
+use warnings;\r
+\r
+use IO::Socket::INET;\r
+use Tpl;\r
+\r
+$SIG{CHLD} = "IGNORE"; # don't create zombies\r
+\r
+our $port = 2000;\r
+\r
+sub handle_client {\r
+ my $client = shift;\r
+\r
+ undef $/;\r
+ my $request = <$client>; # get request (slurp)\r
+\r
+ # read input array, and calculate total\r
+ my ($i,$total);\r
+ my $tpl = Tpl->tpl_map("A(i)", \$i);\r
+ eval { $tpl->tpl_load(\$request); };\r
+ die "received invalid tpl" if $@;\r
+ $total += $i while $tpl->tpl_unpack(1) > 0;\r
+\r
+ # formulate response and send\r
+ my $tpl2 = Tpl->tpl_map("i", \$total);\r
+ $tpl2->tpl_pack(0);\r
+ my $response = $tpl2->tpl_dump();\r
+ print $client $response;\r
+ close $client;\r
+}\r
+\r
+my $server = IO::Socket::INET->new(LocalPort => $port,\r
+ Type => SOCK_STREAM,\r
+ Reuse => 1,\r
+ Listen => 10 )\r
+ or die "Can't listen on port $port: $!\n";\r
+\r
+while (1) {\r
+ my $client = $server->accept();\r
+ next unless $client;\r
+ # new connection\r
+ my $pid = fork;\r
+ die "can't fork: $!\n" unless defined $pid;\r
+ if ($pid > 0) {\r
+ # parent\r
+ close $client;\r
+ } elsif ($pid == 0) {\r
+ # child\r
+ handle_client($client);\r
+ exit(0);\r
+ }\r
+}\r
+close ($server);</tt></pre>\r
+</div></div>\r
+<h4 id="_client">Client</h4>\r
+<div class="paragraph"><p>The client is a simpler program. It constructs the tpl image containing the\r
+integer array (taken from its command-line arguments), connects to the server\r
+and sends the tpl image to it, and then awaits the response tpl. The response\r
+containing the sum is loaded, unpacked and printed.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Client</div>\r
+<div class="content">\r
+<pre><tt>#!/usr/bin/perl\r
+\r
+use strict;\r
+use warnings;\r
+\r
+use IO::Socket::INET;\r
+use Tpl;\r
+\r
+our $port = 2000;\r
+\r
+# construct tpl\r
+my $i;\r
+my $tpl = Tpl->tpl_map("A(i)",\$i);\r
+$tpl->tpl_pack(1) while ($i=shift @ARGV);\r
+my $request = $tpl->tpl_dump();\r
+\r
+# send to server, get response\r
+my $socket = IO::Socket::INET->new("localhost:$port") or die "can't connect";\r
+print $socket $request;\r
+shutdown($socket,1); # done writing (half-close)\r
+undef $/;\r
+my $response = <$socket>; # get reply (slurp)\r
+\r
+# decode response (or print error)\r
+my $total;\r
+my $tpl2 = Tpl->tpl_map("i", \$total);\r
+eval { $tpl2->tpl_load(\$response); };\r
+die "invalid response\n" if $@;\r
+$tpl2->tpl_unpack(0);\r
+print "total is $total\n";</tt></pre>\r
+</div></div>\r
+<h4 id="_running_thise_example">Running thise example</h4>\r
+<div class="paragraph"><p>If the client and server programs are in <tt>client.pl</tt> and <tt>server.pl</tt>, then\r
+you can run the example by starting the server in one window:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>./server.pl</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Then run the client in another window. E.g.,</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>./client.pl 1 2 3 4 5</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The client runs and then exits, printing:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>total is 15</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>You can re-run the client with different arguments. When done, type <tt>Ctrl-C</tt> in\r
+the server window to terminate it.</p></div>\r
+</div>\r
+<div id="footer">\r
+<div id="footer-text">\r
+Version 1.1<br />\r
+Last updated 2009-04-30 21:22:12 EDT\r
+</div>\r
+</div>\r
+</body>\r
+</html>\r
--- /dev/null
+#banner {
+ /* font-size: x-large; */
+ /* background: #ff00ff; */
+ /* height: 100px; */
+}
+
+#topnav {
+ /* background-image: url(img/grad_topnav.png); */
+ /* background-repeat: repeat-y; */
+ /* background-color: #af00af; */
+ /* height: 25px; */
+ margin: 10px 0px 10px 20px;
+ padding: 3px;
+ font-size: 9pt;
+ font-family: sans-serif;
+ /* border-style: solid; */
+ /* border-width: 1px; */
+}
+
+
+#topnav {font-weight: bold}
+#topnav a {font-weight: normal}
+
+h1,p { margin: 0; } /* non-0 margin on firefox */
+
+#mid {
+ background-image: url(img/grad_azure.png);
+ background-repeat: repeat-y;
+ /* background-color: #ffddaa; */
+ padding-top: 20px;
+ margin-bottom: 10px;
+}
+
+#mid img {
+ padding-left: 10px;
+ vertical-align: middle;
+}
+
+a img {
+ border: 0
+}
+
+#nav {
+ background-color: #fff8f1;
+ margin-left: 10px;
+ margin-top: 20px;
+ float: left;
+ padding: 10px;
+ border-style: solid;
+ border-width: 2px;
+ font-family: sans-serif;
+}
+
+
+#nav h2 {
+ font-weight: bold;
+ font-size: 10pt;
+}
+
+#nav h3 {
+ /* font-weight: bold; */
+ padding-left: 5px;
+ /* font-style: oblique; */
+ font-family: sans-serif;
+ font-size: 7pt;
+}
+
+#nav div {
+ font-size: 9pt;
+ padding-left: 15px;
+}
+
+#main {
+ background: #ffffff;
+ margin-top: 20px;
+ margin-left: 170px;
+ padding-left: 20px;
+ height: 100%;
+ /* font-family: sans-serif; */
+}
+
+#main h1 {
+ font-family: sans-serif;
+}
+
+
+.listing {
+ margin: 20px;
+ font-family: sans-serif;
+ font-weight: bold;
+}
+
+.code {
+ padding: 10px;
+ background: #f3f3f3;
+ font-size: 8pt;
+ font-weight: normal;
+ width: 80%;
+ border-style: solid;
+ border-width: 1px;
+}
+
+.code pre {
+ padding-left: 20px;
+ padding-right: 80px;
+}
+
+#formatstrings {
+ margin: 20px;
+}
+
+#footer {
+ /* background: #00ffff; */
+ margin-top: 5px;
+ font-size: small;
+ font-family: sans-serif;
+}
+
+em {
+ font-weight: bold;
+}
+
+hr {
+ height: 0.04em;
+ background: black;
+ margin: 0 10% 0 0;
+}
+
+#footer img {
+ margin-right: 5px;
+ float: right;
+}
+
+.lead {
+ font-family: sans-serif;
+ font-size: larger;
+ font-weight: bold;
+ /* font-style: oblique; */
+ margin: 30px 30px 30px 0px;
+ color: #1122dd;
+}
+
+ol {
+ font-family: monospace;
+ background: #dddddd;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ width: 80%;
+ border-width: 1px;
+ border-style: solid;
+ /* font-size: smaller; */
+}
+
+#main #portrait {
+ float: right;
+ font-size: smaller;
+ font-family: sans-serif;
+ text-align: center;
+ margin: 10px;
+}
--- /dev/null
+/* Workarounds for IE6's broken and incomplete CSS2. */
+
+div.sidebar-content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+div.sidebar-title, div.image-title {
+ font-family: sans-serif;
+ font-weight: bold;
+ margin-top: 0.0em;
+ margin-bottom: 0.5em;
+}
+
+div.listingblock div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock-content {
+ padding-left: 2.0em;
+}
+
+div.exampleblock-content {
+ border-left: 2px solid silver;
+ padding-left: 0.5em;
+}
+
+/* IE6 sets dynamically generated links as visited. */
+div#toc a:visited { color: blue; }
--- /dev/null
+/* Debug borders */
+p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
+/*
+ border: 1px solid red;
+*/
+}
+
+body {
+ margin: 1em 5% 1em 5%;
+}
+
+a {
+ color: blue;
+ text-decoration: underline;
+}
+a:visited {
+ color: fuchsia;
+}
+
+em {
+ font-style: italic;
+}
+
+strong {
+ font-weight: bold;
+}
+
+tt {
+ color: navy;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #527bbd;
+ font-family: sans-serif;
+ margin-top: 1.2em;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+}
+
+h1, h2, h3 {
+ border-bottom: 2px solid silver;
+}
+h2 {
+ padding-top: 0.5em;
+}
+h3 {
+ float: left;
+}
+h3 + * {
+ clear: left;
+}
+
+div.sectionbody {
+ font-family: serif;
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid silver;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+span#author {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+span#email {
+}
+span#revision {
+ font-family: sans-serif;
+}
+
+div#footer {
+ font-family: sans-serif;
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+div#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+div#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+div#preamble,
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-right: 10%;
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.admonitionblock {
+ margin-top: 2.5em;
+ margin-bottom: 2.5em;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ font-family: sans-serif;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+
+div.listingblock {
+ margin-right: 0%;
+}
+div.listingblock > div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock > div.content {
+ padding-left: 2.0em;
+}
+
+div.attribution {
+ text-align: right;
+}
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #527bbd;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 2px solid silver;
+}
+
+div.exampleblock > div.content {
+ border-left: 2px solid silver;
+ padding: 0.5em;
+}
+
+div.verseblock div.content {
+ white-space: pre;
+}
+
+div.imageblock div.content { padding-left: 0; }
+div.imageblock img { border: 1px solid silver; }
+span.image img { border-style: none; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: italic;
+}
+dd > *:first-child {
+ margin-top: 0;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+div.olist2 ol {
+ list-style-type: lower-alpha;
+}
+
+div.tableblock > table {
+ border: 3px solid #527bbd;
+}
+thead {
+ font-family: sans-serif;
+ font-weight: bold;
+}
+tfoot {
+ font-weight: bold;
+}
+
+div.hlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hlist td {
+ padding-bottom: 5px;
+}
+td.hlist1 {
+ vertical-align: top;
+ font-style: italic;
+ padding-right: 0.8em;
+}
+td.hlist2 {
+ vertical-align: top;
+}
+
+@media print {
+ div#footer-badges { display: none; }
+}
+
+div#toctitle {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+div.toclevel2 {
+ margin-left: 2em;
+ font-size: 0.9em;
+}
+div.toclevel3 {
+ margin-left: 4em;
+ font-size: 0.9em;
+}
+div.toclevel4 {
+ margin-left: 6em;
+ font-size: 0.9em;
+}
+#toc {
+ float: right;
+ font-family: sans-serif;
+ border: 1px solid #000;
+ margin: 0px 0px 20px 20px;
+ padding: 0px;
+ background: #f0f0f0;
+ font-size: 80%;
+}
+
+#toc #hdr {
+ color:#ffffff;
+ background:#98b1c4;
+ text-align:center;
+ margin-bottom:5px;
+}
+
+a img {
+ border: 0
+}
+
+#toc a:visited, #toc a:link { color:#000; text-decoration: none }
+#toc a:hover { color:#00f; text-decoration: underline; }
+
+#toc .level2 { margin-left: 1em; margin-top: 2px; margin-bottom: 2px; text-decoration: underline; }
+#toc .level3 { margin-left: 2em; font-size: 0.8em }
+
+.toplink {
+ float: right;
+ font-size: 50%;
+ cursor: pointer;
+}
+
+#topnav {font-weight: bold}
+#topnav a {font-weight: normal}
--- /dev/null
+#toc {
+ float: right;
+ font-family: sans-serif;
+ border: 1px solid #000;
+ margin: 0px 0px 20px 20px;
+ padding: 0px;
+ background: #f0f0f0;
+ font-size: 80%;
+}
+
+#toc #hdr {
+ color:#ffffff;
+ background:#98b1c4;
+ text-align:center;
+ margin-bottom:5px;
+}
+
+a img {
+ border: 0
+}
+
+#toc a:visited, #toc a:link { color:#000; text-decoration: none }
+#toc a:hover { color:#00f; text-decoration: underline; }
+
+#toc .level2 { margin-left: 1em; margin-top: 2px; margin-bottom: 2px; text-decoration: underline; }
+#toc .level3 { margin-left: 2em; font-size: 0.8em }
+
+.toplink {
+ float: right;
+ font-size: 50%;
+ cursor: pointer;
+}
+
+#topnav {font-weight: bold}
+#topnav a {font-weight: normal}
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
+<meta name="generator" content="AsciiDoc 8.3.4" />\r
+<title>tpl User Guide</title>\r
+<link rel="stylesheet" href="./tdh.css" type="text/css" />\r
+<link rel="stylesheet" href="./tdh-quirks.css" type="text/css" />\r
+</head>\r
+<body>\r
+<div id="header">\r
+<h1>tpl User Guide</h1>\r
+<span id="author">Troy D. Hanson</span><br />\r
+<span id="email"><tt><<a href="mailto:thanson@users.sourceforge.net">thanson@users.sourceforge.net</a>></tt></span><br />\r
+<span id="revision">version 1.5,</span>\r
+February 2010\r
+</div>\r
+<div id="preamble">\r
+<div class="sectionbody">\r
+<a style="float: right;" href="http://sourceforge.net/projects/tpl"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=157637&type=16" width="150" height="40" alt="SourceForge.net" /></a>\r
+ <div id="topnav" style="font-size: 9pt; font-family: sans-serif;">\r
+ <a style="padding: 8px;" href="http://sourceforge.net/projects/tpl/">sf.net summary page</a> >\r
+ <a style="padding: 8px;" href="index.html">tpl home</a> >\r
+ tpl User Guide\r
+ <a style="padding: 8px;" href="userguide.pdf">[View PDF]</a>\r
+ </div>\r
+</div>\r
+</div>\r
+<h2 id="_overview">Overview</h2>\r
+<div class="sectionbody">\r
+<div id="toc"></div>\r
+<script>\r
+window.onload=generate_TOC\r
+\r
+/* Author: Mihai Bazon, September 2002\r
+ * <a href="http://students.infoiasi.ro/~mishoo">http://students.infoiasi.ro/~mishoo</a>\r
+ *\r
+ * Table Of Content generator\r
+ * Version: 0.4\r
+ *\r
+ * Feel free to use this script under the terms of the GNU General Public\r
+ * License, as long as you do not remove or alter this notice.\r
+ */\r
+\r
+ /* modified by Troy D. Hanson, September 2006. License: GPL */\r
+\r
+function H_getText(el) {\r
+ var text = "";\r
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
+ if (i.nodeType == 3 /* Node.TEXT_NODE, IE doesn't speak constants */)\r
+ text += i.data;\r
+ else if (i.firstChild != null)\r
+ text += H_getText(i);\r
+ }\r
+ return text;\r
+}\r
+\r
+function TOC_EL(el, text, level) {\r
+ this.element = el;\r
+ this.text = text;\r
+ this.level = level;\r
+}\r
+\r
+function getHeadlines(el) {\r
+ var l = new Array;\r
+ var rx = /[hH]([2-3])/;\r
+ // internal recursive function that scans the DOM tree\r
+ var rec = function (el) {\r
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
+ if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
+ if (rx.exec(i.tagName))\r
+ l[l.length] = new TOC_EL(i, H_getText(i), parseInt(RegExp.$1));\r
+ rec(i);\r
+ }\r
+ }\r
+ }\r
+ rec(el);\r
+ return l;\r
+}\r
+\r
+function generate_TOC() {\r
+ var parent = document.getElementById("toc");\r
+ var toc_hdr = document.createElement("div");\r
+ var toc_hdr_txt = document.createTextNode("CONTENTS");\r
+ toc_hdr.appendChild(toc_hdr_txt);\r
+ /* toc_hdr.setAttribute("id","hdr"); */\r
+ toc_hdr.id = "hdr";\r
+ parent.appendChild(toc_hdr);\r
+ var hs = getHeadlines(document.getElementsByTagName("body")[0]);\r
+ for (var i = 0; i < hs.length; ++i) {\r
+ var hi = hs[i];\r
+ var d = document.createElement("div");\r
+ if (hi.element.id == "") hi.element.id = "gen" + i;\r
+ var a = document.createElement("a");\r
+ a.href = "#" + hi.element.id;\r
+ a.appendChild(document.createTextNode(hi.text));\r
+ d.appendChild(a);\r
+ d.className = "level" + hi.level;\r
+ parent.appendChild(d);\r
+ /*\r
+ if (hi.level == 3) {\r
+ var dvtop = document.createElement("div");\r
+ dvtop.className = "toplink";\r
+ dvtop.appendChild(document.createTextNode("^top^"));\r
+ dvtop.onclick=function(){scrollTo(0,0);};\r
+ hi.element.appendChild(dvtop);\r
+ }\r
+ */\r
+ }\r
+}\r
+</script>\r
+<h3 id="_serialization_in_c">Serialization in C</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Tpl is a library for serializing C data. The data is stored in its natural\r
+binary form. The API is small and tries to stay "out of the way".\r
+Tpl can serialize many C data types, including structures.</p></div>\r
+<h3 id="_uses_for_tpl">Uses for tpl</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Tpl makes a convenient file format. For example, suppose a program needs to\r
+store a list of user names and ids. This can be expressed using the format\r
+string <tt>A(si)</tt>. If the program needs two such lists (say, one for regular\r
+users and one for administrators) this could be expressed as <tt>A(si)A(si)</tt>. It\r
+is easy to read and write this kind of structured data using tpl.</p></div>\r
+<div class="paragraph"><p>Tpl can also be used as an IPC message format. It handles byte order issues\r
+and deframing individual messages off of a stream automatically.</p></div>\r
+<h3 id="_expressing_type">Expressing type</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The "data type" of a tpl is explicitly stated as a format string. There is\r
+never any ambiguity about the type of data stored in a tpl. Some examples:</p></div>\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+<tt>A(is)</tt> is a variable-length array of integer-string pairs\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+<tt>A(is)A(is)</tt> are two such arrays, completely independent of one another\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+<tt>S(ci)</tt> is a structure containing a char and integer\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+<tt>S(ci)#</tt> is a fixed-length array of the latter structure\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+<tt>A(A(i))</tt> is a nested array, that is, an array of integer arrays\r
+</p>\r
+</li>\r
+</ul></div>\r
+<h3 id="_the_tpl_image">The tpl image</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>A tpl image is the serialized form of a tpl, stored in a memory buffer or file,\r
+or written to a file descriptor.</p></div>\r
+<h4 id="_what_8217_s_in_a_tpl_image">What’s in a tpl image?</h4>\r
+<div class="paragraph"><p>There is no need to understand the internal structure of the tpl image. But for the\r
+curious, the image is a strictly defined binary buffer having two sections,\r
+a header and the data. The header encodes the length of the image, its\r
+format string, endian order and other flags. The data section contains the\r
+packed data.</p></div>\r
+<h4 id="_no_framing_needed">No framing needed</h4>\r
+<div class="paragraph"><p>A property of the tpl image is that consecutive images can be written to a stream\r
+without requiring any delimiter between them. The reader making use of\r
+<tt>tpl_gather</tt> (or <tt>tpl_load</tt> in <tt>TPL_FD</tt> mode) will obtain exactly one tpl image at\r
+a time. Therefore tpl images can be used as an IPC message format without any\r
+higher-level framing protocol.</p></div>\r
+<h4 id="_data_portability">Data portability</h4>\r
+<div class="paragraph"><p>A tpl image generated on one kind of CPU will generally be portable to other\r
+CPU types when tpl is used properly. This may be a surprise considering that\r
+tpl is a binary format. But tpl has been carefully designed to make this work.\r
+Each <a href="#types">format character</a> has an associated explicitly-sized type. For\r
+integer and floating point types, whose "endian" or byte-order convention varies\r
+from one CPU to another, tpl automatically and transparently corrects the\r
+endian order (if needed) during the unpacking process. Floating point numbers\r
+present their own <a href="#trouble_with_double">special difficulties</a>. <em>No guarantees\r
+are made with regard to floating point portability.</em> That said, because many\r
+modern CPU’s use IEEE 754 floating point representation, data is likely to be\r
+portable among them.</p></div>\r
+<h3 id="_xml_and_perl">XML and Perl</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p><em>Note: The <tt>tplxml</tt> utility and the Perl module are currently unsupported in tpl 1.5.</em></p></div>\r
+<h4 id="_xml">XML</h4>\r
+<div class="paragraph"><p>While a tpl image is a binary entity, you can view any tpl image in XML format\r
+using the included <tt>tplxml</tt> utility, located in the <tt>lang/perl</tt> directory.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tplxml file.tpl > file.xml\r
+tplxml file.xml > file.tpl</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The utility is bidirectional, as shown. The file extension is not important;\r
+<tt>tplxml</tt> inspects its input to see if it’s tpl or XML. You can also pipe data\r
+into it instead of giving it a filename. The <tt>tplxml</tt> utility is slow. Its\r
+purpose is two-fold: debugging (manual inspection of the data in a tpl), and\r
+interoperability with XML-based programs. The resulting XML is often ten times\r
+the size of the original binary tpl image.</p></div>\r
+<h4 id="_perl">Perl</h4>\r
+<div class="paragraph"><p>There is a Perl module in <tt>lang/perl/Tpl.pm</tt>. The <a href="perl.html">Perl API</a>\r
+is convenient for writing Perl scripts that interoperate with C programs, and\r
+need to pass structured data back and forth. It is written in pure Perl.</p></div>\r
+<h3 id="_platforms">Platforms</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The tpl software was developed for POSIX systems and has been tested on 32- and 64-bit\r
+platforms including:</p></div>\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+Linux\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Solaris\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Mac OS X\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+OpenBSD\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Windows using Visual Studio 2008 or 2010, or Cygwin or MinGW\r
+</p>\r
+</li>\r
+</ul></div>\r
+<h3 id="_bsd_licensed">BSD licensed</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This software is made available under the\r
+<a href="license.html">revised BSD license</a>.\r
+It is free and open source.</p></div>\r
+<h3 id="_download">Download</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Please follow the link to download on the\r
+<a href="http://tpl.sourceforge.net">tpl website</a>.</p></div>\r
+<h3 id="_getting_help">Getting help</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>If you need help, you are welcome to email the author at\r
+<a href="mailto:thanson@users.sourceforge.net">thanson@users.sourceforge.net</a>.</p></div>\r
+<h3 id="_resources">Resources</h3><div style="clear:left"></div>\r
+<div class="dlist"><dl>\r
+<dt class="hdlist1">\r
+News\r
+</dt>\r
+<dd>\r
+<p>\r
+ The author has a news feed for <a href="http://troydhanson.wordpress.com/feed/">software updates</a> <span class="image">\r
+<img src="img/rss.png" alt="(RSS)" title="(RSS)" />\r
+</span>.\r
+</p>\r
+</dd>\r
+</dl></div>\r
+</div>\r
+<h2 id="_build_and_install">Build and install</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>Tpl has no dependencies on libraries other than the system C library. You\r
+can simply copy the tpl source into your project, so you have no dependencies.\r
+Alternatively, you can build tpl as a library and link it to your program.</p></div>\r
+<h3 id="_as_source">As source</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The simplest way to use tpl is to copy the source files <tt>tpl.h</tt> and <tt>tpl.c</tt>\r
+(from the <tt>src/</tt> directory) right into your project, and build them with the\r
+rest of your source files. No special compiler flags are required.</p></div>\r
+<h3 id="_as_a_library">As a library</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Alternatively, to build tpl as a library, from the top-level directory, run:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>./configure\r
+make\r
+make install</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This installs a static library <tt>libtpl.a</tt> and a shared library (e.g.,\r
+<tt>libtpl.so</tt>), if your system supports them, in standard places. The installation\r
+directory can be customized using <tt>./configure --prefix=/some/directory</tt>. Run\r
+<tt>configure --help</tt> for further options.</p></div>\r
+<h4 id="_test_suite">Test suite</h4>\r
+<div class="paragraph"><p>You can compile and run the built-in test suite by running:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>cd tests/\r
+make</tt></pre>\r
+</div></div>\r
+<h3 id="_on_windows">On Windows</h3><div style="clear:left"></div>\r
+<h4 id="_dll">DLL</h4>\r
+<div class="paragraph"><p>On the tpl home page, a Visual Studio 2008 solution package is available for\r
+download. This zip file contains pre-built 32- and 64-bit versions of tpl as a\r
+DLL. If you like, you can build the DLL yourself using VS2008 or VS2010 (the\r
+free Express Edition is sufficient) by opening the solution file and choosing\r
+Build Solution.</p></div>\r
+<h4 id="_non_dll_usage">Non-DLL usage</h4>\r
+<div class="paragraph"><p>Alternatively, tpl can be used directly (instead of as a DLL) by compiling\r
+the tpl sources right into your program. To do this, add <tt>tpl.c</tt>, <tt>tpl.h</tt>,\r
+<tt>win/mman.h</tt> and <tt>win/mmap.c</tt> to your program’s source and header files and\r
+add the preprocessor definition <tt>TPL_NOLIB</tt>.</p></div>\r
+<h4 id="_mingw_cygwin">MinGW/Cygwin</h4>\r
+<div class="paragraph"><p>Prior to tpl release 1.5, using tpl on Windows required building it with MinGW\r
+or Cygwin. This is no longer necessary. If you want to build it that way anyway,\r
+use the non-Windows (i.e. tar.bz2) tpl download and follow the "configure; make;\r
+make install" approach.</p></div>\r
+</div>\r
+<h2 id="_api_concepts">API concepts</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>To use tpl, you need to know the order in which to call the API functions, and\r
+the background concepts of format string, arrays and index numbers.</p></div>\r
+<h3 id="_order_of_functions">Order of functions</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Creating a tpl is always the first step, and freeing it is the last step. In\r
+between, you either pack and dump the tpl (if you’re serializing data) or you\r
+load a tpl image and unpack it (if you’re deserializing data).</p></div>\r
+<div class="tableblock">\r
+<table rules="none"\r
+width="50%"\r
+frame="border"\r
+cellspacing="0" cellpadding="4">\r
+<caption class="title">Order of usage</caption>\r
+<col width="9%" />\r
+<col width="45%" />\r
+<col width="45%" />\r
+<thead valign="top">\r
+<tr>\r
+<th align="center">Step </th>\r
+<th align="center"> If you’re serializing…</th>\r
+<th align="center"> If you’re deserializing…</th>\r
+</tr>\r
+</thead>\r
+<tbody valign="top">\r
+<tr>\r
+<td align="center"><p class="table">1.</p></td>\r
+<td align="center"><p class="table"><tt>tpl_map()</tt></p></td>\r
+<td align="center"><p class="table"><tt>tpl_map()</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table">2.</p></td>\r
+<td align="center"><p class="table"><tt>tpl_pack()</tt></p></td>\r
+<td align="center"><p class="table"><tt>tpl_load()</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table">3.</p></td>\r
+<td align="center"><p class="table"><tt>tpl_dump()</tt></p></td>\r
+<td align="center"><p class="table"><tt>tpl_unpack()</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table">4.</p></td>\r
+<td align="center"><p class="table"><tt>tpl_free()</tt></p></td>\r
+<td align="center"><p class="table"><tt>tpl_free()</tt></p></td>\r
+</tr>\r
+</tbody>\r
+</table>\r
+</div>\r
+<h3 id="format">Format string</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>When a tpl is created using <tt>tpl_map()</tt>, its data type is expressed as a format\r
+string. Each character in the format string has an associated argument of a\r
+specific type. For example, this is how a format string and its arguments are\r
+passed in to <tt>tpl_map</tt>:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tpl_node *tn;\r
+char c;\r
+int i[10];\r
+tn = tpl_map("ci#", &c, i, 10); /* ci# is our format string */</tt></pre>\r
+</div></div>\r
+<div class="tableblock" id="types">\r
+<table rules="none"\r
+width="90%"\r
+frame="border"\r
+cellspacing="0" cellpadding="4">\r
+<caption class="title">Supported format characters</caption>\r
+<col width="11%" />\r
+<col width="44%" />\r
+<col width="44%" />\r
+<thead valign="top">\r
+<tr>\r
+<th align="center">Type </th>\r
+<th align="left"> Description </th>\r
+<th align="left"> Required argument type</th>\r
+</tr>\r
+</thead>\r
+<tbody valign="top">\r
+<tr>\r
+<td align="center"><p class="table"><tt>j</tt></p></td>\r
+<td align="left"><p class="table">16-bit signed int</p></td>\r
+<td align="left"><p class="table">int16_t* or equivalent</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>v</tt></p></td>\r
+<td align="left"><p class="table">16-bit unsigned int</p></td>\r
+<td align="left"><p class="table">uint16_t* or equivalent</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>i</tt></p></td>\r
+<td align="left"><p class="table">32-bit signed int</p></td>\r
+<td align="left"><p class="table">int32_t* or equivalent</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>u</tt></p></td>\r
+<td align="left"><p class="table">32-bit unsigned int</p></td>\r
+<td align="left"><p class="table">uint32_t* or equivalent</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>I</tt></p></td>\r
+<td align="left"><p class="table">64-bit signed int</p></td>\r
+<td align="left"><p class="table">int64_t* or equivalent</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>U</tt></p></td>\r
+<td align="left"><p class="table">64-bit unsigned int</p></td>\r
+<td align="left"><p class="table">uint64_t* or equivalent</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>c</tt></p></td>\r
+<td align="left"><p class="table">character (byte)</p></td>\r
+<td align="left"><p class="table">char*</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>s</tt></p></td>\r
+<td align="left"><p class="table">string</p></td>\r
+<td align="left"><p class="table">char**</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>f</tt></p></td>\r
+<td align="left"><p class="table">64-bit double precision float</p></td>\r
+<td align="left"><p class="table">double* (varies by platform)</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>#</tt></p></td>\r
+<td align="left"><p class="table">array length; modifies preceding <tt>iujvIUcsf</tt> or <tt>S(…)</tt></p></td>\r
+<td align="left"><p class="table">int</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>B</tt></p></td>\r
+<td align="left"><p class="table">binary buffer (arbitrary-length)</p></td>\r
+<td align="left"><p class="table">tpl_bin*</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>S</tt></p></td>\r
+<td align="left"><p class="table">structure (…)</p></td>\r
+<td align="left"><p class="table">struct *</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>$</tt></p></td>\r
+<td align="left"><p class="table">nested structure (…)</p></td>\r
+<td align="left"><p class="table">none</p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table"><tt>A</tt></p></td>\r
+<td align="left"><p class="table">array (…)</p></td>\r
+<td align="left"><p class="table">none</p></td>\r
+</tr>\r
+</tbody>\r
+</table>\r
+</div>\r
+<h4 id="_explicit_sizes">Explicit sizes</h4>\r
+<div class="paragraph"><p>The sizes of data types such as <tt>long</tt> and <tt>double</tt> vary by platform. This must\r
+be kept in mind because most tpl format characters require a pointer argument to\r
+a specific-sized type, listed above. You can use explicit-sized types such as\r
+<tt>int32_t</tt> (defined in <tt>inttypes.h</tt>) in your program if you find this helpful.</p></div>\r
+<h5 id="trouble_with_double">The trouble with double</h5>\r
+<div class="paragraph"><p>Unfortunately there are no standard explicit-sized floating-point types-- no\r
+<tt>float64_t</tt>, for example. If you plan to serialize <tt>double</tt> on your platform\r
+using tpl’s <tt>f</tt> format character, first be sure that your <tt>double</tt> is 64 bits.\r
+Second, if you plan to deserialize it on a different kind of CPU, be sure that\r
+both CPU’s use the same floating-point representation such as IEEE 754.</p></div>\r
+<h3 id="arrays">Arrays</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Arrays come in two kinds: <strong>fixed-length</strong> and <strong>variable-length</strong> arrays.\r
+Intuitively, they can be thought of like conventional C arrays and linked lists.\r
+In general, use fixed-length arrays if possible, and variable-length arrays\r
+if necessary. The variable-length arrays support more complex data types, and\r
+give or receive the elements to your program one by one.</p></div>\r
+<h4 id="_fixed_length_vs_variable_length_arrays">Fixed-length vs. Variable-length arrays</h4>\r
+<div class="dlist"><dl>\r
+<dt class="hdlist1">\r
+Notation\r
+</dt>\r
+<dd>\r
+<p>\r
+ Fixed-length arrays are denoted like <tt>i#</tt> (a simple type followed by one or\r
+ more <tt>#</tt> signs), but variable-length arrays are denoted like <tt>A(i)</tt>.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+Element handling\r
+</dt>\r
+<dd>\r
+<p>\r
+ All the elements of a fixed-length array are packed or unpacked at once. But\r
+ the elements of a variable-length array are packed or unpacked one by one.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+Array length\r
+</dt>\r
+<dd>\r
+<p>\r
+ The number of elements in a fixed-length array is specified before use--\r
+ before any data is packed. But variable-length arrays do not have a fixed\r
+ element count. They can have any number of elements packed into them. When\r
+ unpacking a variable-length array, they are unpacked one by one until they\r
+ are exhausted.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+Element types\r
+</dt>\r
+<dd>\r
+<p>\r
+ Elements of fixed-length arrays can be the integer, byte, double, string\r
+ types or structures. (This excludes format characters <tt>BA</tt>). Fixed-length\r
+ arrays can also be multi-dimensional like <tt>i##</tt>. Variable-length arrays can\r
+ have simple or complex elements-- for example, an array of ints <tt>A(i)</tt>, an\r
+ array of int/double pairs <tt>A(if)</tt>, or even nested arrays like <tt>A(A(if))</tt>.\r
+</p>\r
+</dd>\r
+</dl></div>\r
+<div class="paragraph"><p>Before explaining all the concepts, it’s illustrative to see how both kinds of\r
+arrays are used. Let’s pack the integers 0 through 9 both ways.</p></div>\r
+<div class="listingblock" id="fixed_pack">\r
+<div class="title">Packing 0-9 as a fixed-length array</div>\r
+<div class="content">\r
+<pre><tt>#include "tpl.h"\r
+int main() {\r
+ tpl_node *tn;\r
+ int x[] = {0,1,2,3,4,5,6,7,8,9};\r
+\r
+ tn = tpl_map("i#", x, 10);\r
+ tpl_pack(tn,0); /* pack all 10 elements at once */\r
+ tpl_dump(tn, TPL_FILE, "/tmp/fixed.tpl");\r
+ tpl_free(tn);\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Note that the length of the fixed-length array (10) was passed as an argument to\r
+<tt>tpl_map()</tt>. The corresponding unpacking <a href="#fixed_unpack">example</a> is listed\r
+further below. Now let’s see how we would pack 0-9 as a variable-length array:</p></div>\r
+<div class="listingblock">\r
+<div class="title">Packing 0-9 as a variable-length array</div>\r
+<div class="content">\r
+<pre><tt>#include "tpl.h"\r
+int main() {\r
+ tpl_node *tn;\r
+ int x;\r
+\r
+ tn = tpl_map("A(i)", &x);\r
+ for(x = 0; x < 10; x++) tpl_pack(tn,1); /* pack one element at a time */\r
+ tpl_dump(tn, TPL_FILE, "/tmp/variable.tpl");\r
+ tpl_free(tn);\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Notice how we called <tt>tpl_pack</tt> in a loop, once for each element 0-9. Again,\r
+there is a corresponding unpacking <a href="#var_unpack">example</a> shown later in the\r
+guide. You might also notice that this time, we passed 1 as the final argument\r
+to tpl_pack. This is an index number designating which variable-length array\r
+we’re packing. In this case, there is only one.</p></div>\r
+<h4 id="index">Index numbers</h4>\r
+<div class="paragraph"><p>Index numbers identify a particular variable-length array in the format string.\r
+Each <tt>A(…)</tt> in a format string has its own index number. The index numbers\r
+are assigned left-to-right starting from 1. Examples:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>A(i) /* index number 1 */\r
+A(i)A(i) /* index numbers 1 and 2 */\r
+A(A(i)) /* index numbers 1 and 2 (order is independent of nesting) */</tt></pre>\r
+</div></div>\r
+<h5 id="_special_index_number_0">Special index number 0</h5>\r
+<div class="paragraph"><p>The special index number 0 designates all the format characters that are not\r
+inside an <tt>A(…)</tt>. Examples of what index 0 does (and does not) designate:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>S(ius) /* index 0 designates the whole thing */\r
+iA(c)u /* index 0 designates the i and the u */\r
+c#A(i)S(ci) /* index 0 designates the c# and the S(ci) */</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>An index number is passed to <tt>tpl_pack</tt> and <tt>tpl_unpack</tt> to specify which\r
+variable-length array (or non-array, in the case of index number 0) to act upon.</p></div>\r
+<h3 id="_integers">Integers</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The array examples <a href="#fixed_pack">above</a> demonstrated how integers could be\r
+packed. We’ll show some further examples here of unpacking integers and dealing\r
+with multi-dimensional arrays. The same program could be used to demonstrate\r
+working with byte, 16-bit shorts, 32-bit or 64-bit signed and unsigned integers\r
+with only a change to the data type and the format character.</p></div>\r
+<div class="listingblock" id="fixed_unpack">\r
+<div class="title">Unpacking 0-9 from a fixed-length array</div>\r
+<div class="content">\r
+<pre><tt>#include "tpl.h"\r
+int main() {\r
+ tpl_node *tn;\r
+ int x[10];\r
+\r
+ tn = tpl_map("i#", x, 10);\r
+ tpl_load(tn, TPL_FILE, "/tmp/fixed.tpl");\r
+ tpl_unpack(tn,0); /* unpack all 10 elements at once */\r
+ tpl_free(tn);\r
+ /* now do something with x[0]...x[9].. (not shown */\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>For completeness, let’s also see how to unpack a variable-length integer array.</p></div>\r
+<div class="listingblock" id="var_unpack">\r
+<div class="title">Unpacking 0-9 from a variable-length array</div>\r
+<div class="content">\r
+<pre><tt>#include "tpl.h"\r
+int main() {\r
+ tpl_node *tn;\r
+ int x;\r
+\r
+ tn = tpl_map("A(i)", &x);\r
+ tpl_load(tn, TPL_FILE, "/tmp/variable.tpl");\r
+ while (tpl_unpack(tn,1) > 0) printf("%d\n",x); /* unpack one by one */\r
+ tpl_free(tn);\r
+}</tt></pre>\r
+</div></div>\r
+<h4 id="multidim_int">Multi-dimensional arrays</h4>\r
+<div class="paragraph"><p>A multi-dimensional matrix of integers can be packed and unpacked the same way\r
+as any fixed-length array.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>int xy[XDIM][YDIM];\r
+...\r
+tn = tpl_map("i##", xy, XDIM, YDIM);\r
+tpl_pack(tn, 0);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This single call to <tt>tpl_pack</tt> packs the entire matrix.</p></div>\r
+<h3 id="_strings">Strings</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Tpl can serialize C strings. A different format is used for <tt>char*</tt> vs. <tt>char[ ]</tt>\r
+as described below. Let’s look at <tt>char*</tt> first:</p></div>\r
+<div class="listingblock">\r
+<div class="title">Packing a string</div>\r
+<div class="content">\r
+<pre><tt> #include "tpl.h"\r
+\r
+ int main() {\r
+ tpl_node *tn;\r
+ char *s = "hello, world!";\r
+ tn = tpl_map("s", &s);\r
+ tpl_pack(tn,0); /* copies "hello, world!" into the tpl */\r
+ tpl_dump(tn,TPL_FILE,"string.tpl");\r
+ tpl_free(tn);\r
+ }</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The <tt>char*</tt> must point to a null-terminated string or be a <tt>NULL</tt> pointer.</p></div>\r
+<div class="paragraph"><p>When deserializing (unpacking) a C string, space for it will be allocated\r
+automatically, but you are responsible for freeing it (unless it is <tt>NULL</tt>):</p></div>\r
+<div class="listingblock">\r
+<div class="title">Unpacking a string</div>\r
+<div class="content">\r
+<pre><tt> #include "tpl.h"\r
+\r
+ int main() {\r
+ tpl_node *tn;\r
+ char *s;\r
+ tn = tpl_map("s", &s);\r
+ tpl_load(tn,TPL_FILE,"string.tpl");\r
+ tpl_unpack(tn,0); /* allocates space, points s to "hello, world!" */\r
+ printf("unpacked %s\n", s);\r
+ free(s); /* our responsibility to free s */\r
+ tpl_free(tn);\r
+ }</tt></pre>\r
+</div></div>\r
+<h4 id="_char_vs_char">char* vs char[ ]</h4>\r
+<div class="paragraph"><p>The <tt>s</tt> format character is only for use with <tt>char*</tt> types. In the example\r
+above, <tt>s</tt> is a <tt>char*</tt>. If it had been a <tt>char s[14]</tt>, we would use the format\r
+characters <tt>c#</tt> to pack or unpack it, as a fixed-length character array. (This\r
+unpacks the characters "in-place", instead of into a dynamically allocated\r
+buffer). Also, a fixed-length buffer described by <tt>c#</tt> need not be\r
+null-terminated.</p></div>\r
+<h4 id="_arrays_of_strings">Arrays of strings</h4>\r
+<div class="paragraph"><p>You can use fixed- or variable-length arrays of strings in tpl. An example of\r
+packing a fixed-length two-dimensional array of strings is shown here.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>char *labels[2][3] = { {"one", "two", "three"},\r
+ {"eins", "zwei", "drei" } };\r
+tpl_node *tn;\r
+tn = tpl_map("s##", labels, 2, 3);\r
+tpl_pack(tn,0);\r
+tpl_dump(tn,TPL_FILE,filename);\r
+tpl_free(tn);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Later, when unpacking these strings, the programmer must remember to free them\r
+one by one, after they are no longer needed.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>char *olabels[2][3];\r
+int i,j;</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map("s##", olabels, 2, 3);\r
+tpl_load(tn,TPL_FILE,filename);\r
+tpl_unpack(tn,0);\r
+tpl_free(tn);</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>for(i=0;i<2;i++) {\r
+ for(j=0;j<3;j++) {\r
+ printf("%s\n", olabels[i][j]);\r
+ free(olabels[i][j]);\r
+ }\r
+}</tt></pre>\r
+</div></div>\r
+<h3 id="_binary_buffers">Binary buffers</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Packing an arbitrary-length binary buffer (tpl format character <tt>B</tt>) makes use\r
+of the <tt>tpl_bin</tt> structure. You must declare this structure and populate it\r
+with the address and length of the binary buffer to be packed.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Packing a binary buffer</div>\r
+<div class="content">\r
+<pre><tt> #include "tpl.h"\r
+ #include <sys/time.h>\r
+\r
+ int main() {\r
+ tpl_node *tn;\r
+ tpl_bin tb;\r
+\r
+ /* we'll use a timeval as our guinea pig */\r
+ struct timeval tv;\r
+ gettimeofday(&tv,NULL);\r
+\r
+ tn = tpl_map( "B", &tb );\r
+ tb.sz = sizeof(struct timeval); /* size of buffer to pack */\r
+ tb.addr = &tv; /* address of buffer to pack */\r
+ tpl_pack( tn, 0 );\r
+ tpl_dump(tn, TPL_FILE, "bin.tpl");\r
+ tpl_free(tn);\r
+ }</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>When you unpack a binary buffer, tpl will automatically allocate it, and will\r
+populate your <tt>tpl_bin</tt> structure with its address and length. You are\r
+responsible for eventually freeing the buffer.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Unpacking a binary buffer</div>\r
+<div class="content">\r
+<pre><tt> #include "tpl.h"\r
+\r
+ int main() {\r
+ tpl_node *tn;\r
+ tpl_bin tb;\r
+\r
+ tn = tpl_map( "B", &tb );\r
+ tpl_load( tn, TPL_FILE, "bin.tpl" );\r
+ tpl_unpack( tn, 0 );\r
+ tpl_free(tn);\r
+\r
+ printf("binary buffer of length %d at address %p\n", tb.sz, tb.addr);\r
+ free(tb.addr); /* our responsibility to free it */\r
+ }</tt></pre>\r
+</div></div>\r
+<h3 id="_structures">Structures</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>You can use tpl to pack and unpack structures, and arrays of structures.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>struct ci {\r
+ char c;\r
+ int i;\r
+};\r
+struct ci s = {'a', 1};</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map("S(ci)", &s); /* pass structure address */\r
+tpl_pack(tn, 0);\r
+tpl_dump(tn, TPL_FILE, "struct.tpl");\r
+tpl_free(tn);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>As shown, omit the individual arguments for the format characters inside the\r
+parenthesis. The exception is for fixed-length arrays; when <tt>S(…)</tt> contains a\r
+<tt>#</tt> character, its length argument is required: <tt>tpl_map("S(f#i)", &s, 10);</tt></p></div>\r
+<div class="paragraph"><p>When using the <tt>S(…)</tt> format, the only characters allowed inside the\r
+parentheses are <tt>iujvcsfIU#$()</tt>.</p></div>\r
+<h4 id="_structure_arrays">Structure arrays</h4>\r
+<div class="paragraph"><p>Arrays of structures are the same as simple arrays. Fixed- or variable- length\r
+arrays are supported.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>struct ci sa[100], one;</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map("S(ci)#", sa, 100); /* fixed-length array of 100 structures */\r
+tn = tpl_map("A(S(ci))", &one); /* variable-length array (one at a time)*/</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The differences between fixed- and variable-length arrays are explained in the\r
+<a href="#arrays">Arrays</a> section.</p></div>\r
+<h4 id="_nested_structures">Nested structures</h4>\r
+<div class="paragraph"><p>When dealing with nested structures, the outermost structure uses the <tt>S</tt> format\r
+character, and the inner nested structures use the <tt>$</tt> format. Only the\r
+<em>outermost</em> structure’s address is given to <tt>tpl_map</tt>.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>struct inner_t {\r
+ char a;\r
+}</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>struct outer_t {\r
+ char b;\r
+ struct inner_t i;\r
+}</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tpl_node *tn;\r
+struct outer_t outer = {'b', {'a'}};</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map("S(c$(c))", &outer);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Structures can nest to any level. Currently tpl does not support fixed-length\r
+array suffixes on inner structures. However the outermost structure can have a\r
+length suffix even if it contains some nested structures.</p></div>\r
+<h3 id="_linked_lists">Linked lists</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>While tpl has no specific data type for a linked list, the technique for\r
+packing them is illustrated here. First describe your list element as a\r
+format string and then surround it with <tt>A(…)</tt> to describe it as\r
+variable-length array. Then, using a temporary variable, iterate over each\r
+list element, copying it to the temporary variable and packing it.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>struct element {\r
+ char c;\r
+ int i;\r
+ struct element *next;\r
+}</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>struct element *list, *i, tmp;\r
+tpl_node *tn;</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>/* add some elements to list.. (not shown)*/</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map("A(ci)", &tmp);\r
+for(i = list; i != NULL; i=i->next) {\r
+ tmp = *i;\r
+ tpl_pack(tn, 1);\r
+}\r
+tpl_dump(tn,TPL_FILE,"list.tpl");\r
+tpl_free(tn);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Unpacking is similar. The <tt>for</tt> loop is just replaced with:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>while( tpl_unpack(tn,1) > 0) {\r
+ struct element *newelt = malloc(sizeof(struct element));\r
+ *newelt = tmp;\r
+ add_to_list(list, newelt);\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>As you can see, tpl does not reinstate the whole list at once-- just one\r
+element at a time. You need to link the elements manually. A future release of\r
+tpl may support <em>pointer swizzling</em> to make this easier.</p></div>\r
+</div>\r
+<h2 id="_api">API</h2>\r
+<div class="sectionbody">\r
+<h3 id="tpl_map">tpl_map</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The only way to create a tpl is to call <tt>tpl_map()</tt>. The first argument is the\r
+<a href="#format">format string</a>. This is followed by a list of arguments as required by\r
+the particular characters in the format string. E.g,</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tpl_node *tn;\r
+int i;\r
+tn = tpl_map( "A(i)", &i );</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The function creates a mapping between the items in the format string and the C\r
+program variables whose addresses are given. Later, the C variables will be read\r
+or written as the tpl is packed or unpacked.</p></div>\r
+<div class="paragraph"><p>This function returns a <tt>tpl_node*</tt> on success, or <tt>NULL</tt> on failure.</p></div>\r
+<h3 id="tpl_pack">tpl_pack</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The function <tt>tpl_pack()</tt> packs data into a tpl. The arguments to\r
+<tt>tpl_pack()</tt> are a <tt>tpl_node*</tt> and an <a href="#index">index number</a>.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map("A(i)A(c)", &i, &c);\r
+for(i=0; i<10; i++) tpl_pack(tn, 1); /* pack 0-9 into index 1 */\r
+for(c='a; c<='z'; c++) tpl_pack(tn, 2); /* pack a-z into index 2 */</tt></pre>\r
+</div></div>\r
+<div class="sidebarblock">\r
+<div class="sidebar-content">\r
+<div class="sidebar-title">Data is copied when packed</div>\r
+<div class="paragraph"><p>Every call to <tt>tpl_pack()</tt> immediately <em>copies</em> the data being packed. Thus\r
+the program is free to immediately overwrite or re-use the packed variables.</p></div>\r
+</div></div>\r
+<h4 id="_index_number_0">Index number 0</h4>\r
+<div class="paragraph"><p>It is necessary to pack index number 0 only if the format string contains\r
+characters that are not inside an <tt>A(…)</tt>, such as the <tt>i</tt> in the format string\r
+<tt>iA(c)</tt>.</p></div>\r
+<h4 id="_variable_length_arrays">Variable-length arrays</h4>\r
+<h5 id="_adding_elements_to_an_array">Adding elements to an array</h5>\r
+<div class="paragraph"><p>To add elements to a variable-length array, call <tt>tpl_pack()</tt> repeatedly. Each\r
+call adds another element to the array.</p></div>\r
+<h5 id="_zero_length_arrays_are_ok">Zero-length arrays are ok</h5>\r
+<div class="paragraph"><p>It’s perfectly acceptable to pack nothing into a variable-length array,\r
+resulting in a zero-length array.</p></div>\r
+<h5 id="nested_pack">Packing nested arrays</h5>\r
+<div class="paragraph"><p>In a format string containing a nested, variable-length array, such as\r
+<tt>A(A(s))</tt>, the inner, child array should be packed prior to the parent array.</p></div>\r
+<div class="paragraph"><p>When you pack a parent array, a "snapshot" of the current child array is placed\r
+into the parent’s new element. Packing a parent array also empties the child\r
+array. This way, you can pack new data into the child, then pack the parent\r
+again. This creates distinct parent elements which each contain distinct child\r
+arrays.</p></div>\r
+<div class="admonitionblock">\r
+<table><tr>\r
+<td class="icon">\r
+<div class="title">Tip</div>\r
+</td>\r
+<td class="content">When dealing with nested arrays like <tt>A(A(i))</tt>, <em>pack</em> them from the "inside\r
+out" (child first), but <em>unpack</em> them from the "outside in" (parent first).</td>\r
+</tr></table>\r
+</div>\r
+<div class="paragraph"><p>The example below creates a tpl having the format string <tt>A(A(c))</tt>.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Packing nested arrays</div>\r
+<div class="content">\r
+<pre><tt>#include "tpl.h"\r
+\r
+int main() {\r
+ char c;\r
+ tpl_node *tn;\r
+\r
+ tn = tpl_map("A(A(c))", &c);\r
+\r
+ for(c='a'; c<'c'; c++) tpl_pack(tn,2); /* pack child (twice) */\r
+ tpl_pack(tn, 1); /* pack parent */\r
+\r
+ for(c='1'; c<'4'; c++) tpl_pack(tn,2); /* pack child (three times) */\r
+ tpl_pack(tn, 1); /* pack parent */\r
+\r
+ tpl_dump(tn, TPL_FILE, "test40.tpl");\r
+ tpl_free(tn);\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This creates a nested array in which the parent has two elements: the first\r
+element is the two-element nested array <em>a</em>, <em>b</em>; and the second element is\r
+the three-element nested array <em>1</em>, <em>2</em>, <em>3</em>.\r
+The <a href="#nested_unpack">nested unpacking example</a> shows how this tpl is unpacked.</p></div>\r
+<h3 id="tpl_dump">tpl_dump</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>After packing a tpl, <tt>tpl_dump()</tt> is used to write the tpl image to a file,\r
+memory buffer or file descriptor. The corresponding modes are shown below. A\r
+final mode is for querying the output size without actually performing the dump.</p></div>\r
+<div class="tableblock">\r
+<table rules="none"\r
+width="80%"\r
+frame="border"\r
+cellspacing="0" cellpadding="4">\r
+<col width="30%" />\r
+<col width="70%" />\r
+<thead valign="top">\r
+<tr>\r
+<th align="center">Write to… </th>\r
+<th align="left">Usage</th>\r
+</tr>\r
+</thead>\r
+<tbody valign="top">\r
+<tr>\r
+<td align="center"><p class="table">file</p></td>\r
+<td align="left"><p class="table"><tt>tpl_dump(tn, TPL_FILE, "file.tpl" );</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table">file descriptor</p></td>\r
+<td align="left"><p class="table"><tt>tpl_dump(tn, TPL_FD, 2);</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table">memory</p></td>\r
+<td align="left"><p class="table"><tt>tpl_dump(tn, TPL_MEM, &addr, &len );</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table">caller’s memory</p></td>\r
+<td align="left"><p class="table"><tt>tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buf, sizeof(buf));</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="center"><p class="table">just get size</p></td>\r
+<td align="left"><p class="table"><tt>tpl_dump(tn, TPL_GETSIZE, &sz);</tt></p></td>\r
+</tr>\r
+</tbody>\r
+</table>\r
+</div>\r
+<div class="paragraph"><p>The first argument is the <tt>tpl_node*</tt> and the second is one of these constants:</p></div>\r
+<div class="dlist"><dl>\r
+<dt class="hdlist1">\r
+<tt>TPL_FILE</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ Writes the tpl to a file whose name is given in the following argument.\r
+ The file is created with permissions 664 (<tt>rw-rw-r--</tt>) unless further\r
+ restricted by the process <tt>umask</tt>.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<tt>TPL_FD</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ Writes the tpl to the file descriptor given in the following argument.\r
+ The descriptor can be either blocking or non-blocking, but will busy-loop\r
+ if non-blocking and the contents cannot be written immediately.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<tt>TPL_MEM</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ Writes the tpl to a memory buffer. The following two arguments must be a\r
+ <tt>void**</tt> and a <tt>size_t*</tt>. The function will allocate a buffer and store\r
+ its address and length into these locations. The caller is responsible to\r
+ <tt>free()</tt> the buffer when done using it.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<tt>TPL_MEM|TPL_PREALLOCD</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ Writes the tpl to a memory buffer that the caller has already allocated or\r
+ declared. The following two arguments must be a <tt>void*</tt> and a <tt>size_t</tt>\r
+ specifying the buffer address and size respectively. (If the buffer is of\r
+ insufficient size to receive the tpl dump, the function will return -1).\r
+ This mode can be useful in conjunction with <tt>tpl_load</tt> in <tt>TPL_EXCESS_OK</tt>\r
+ mode, as shown <a href="#excess_ok">here.</a>\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<tt>TPL_GETSIZE</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ This special mode does not actually dump the tpl. Instead it places the size\r
+ that the dump <em>would</em> require into the <tt>uint32_t</tt> pointed to by the\r
+ following argument.\r
+</p>\r
+</dd>\r
+</dl></div>\r
+<div class="paragraph"><p>The return value is 0 on success, or -1 on error.</p></div>\r
+<div class="paragraph"><p>The <tt>tpl_dump()</tt> function does not free the tpl. Use <tt>tpl_free()</tt> to release\r
+the tpl’s resources when done.</p></div>\r
+<div class="admonitionblock">\r
+<table><tr>\r
+<td class="icon">\r
+<div class="title">Tip</div>\r
+</td>\r
+<td class="content">\r
+<div class="title">Back-to-back tpl images require no delimiter</div>If you want to store a series of tpl images, or transmit sequential tpl images\r
+over a socket (perhaps as messages to another program), you can simply dump them\r
+sequentially without needing to add any delimiter for the individual tpl images.\r
+Tpl images are internally delimited, so <tt>tpl_load</tt> will read just one at a time\r
+even if multiple images are contiguous.</td>\r
+</tr></table>\r
+</div>\r
+<h3 id="tpl_load">tpl_load</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This API function reads a previously-dumped tpl image from a file, memory\r
+buffer or file descriptor, and prepares it for subsequent unpacking. The format\r
+string specified in the preceding call to <tt>tpl_map()</tt> will be cross-checked\r
+for equality with the format string stored in the tpl image.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map( "A(i)", &i );\r
+tpl_load( tn, TPL_FILE, "demo.tpl" );</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The first argument to <tt>tpl_load()</tt> is the <tt>tpl_node*</tt>. The second argument is\r
+one of the constants:</p></div>\r
+<div class="dlist"><dl>\r
+<dt class="hdlist1">\r
+<tt>TPL_FILE</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ Loads the tpl from the file named in the following argument. It is also\r
+ possible to bitwise-OR this flag with <tt>TPL_EXCESS_OK</tt> as explained below.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<tt>TPL_MEM</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ Loads the tpl from a memory buffer. The following two arguments must be a\r
+ <tt>void*</tt> and a <tt>size_t</tt>, specifying the buffer address and size,\r
+ respectively. The caller must not free the memory buffer until after\r
+ freeing the tpl with <tt>tpl_free()</tt>. (If the caller wishes to hand over\r
+ responsibility for freeing the memory buffer, so that it’s automatically\r
+ freed along with the tpl when <tt>tpl_free()</tt> is called, the constant\r
+ <tt>TPL_UFREE</tt> may be bitwise-OR’d with <tt>TPL_MEM</tt> to achieve this).\r
+ Furthermore, <tt>TPL_MEM</tt> may be bitwise-OR’d with <tt>TPL_EXCESS_OK</tt>, explained\r
+ below.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<tt>TPL_FD</tt>\r
+</dt>\r
+<dd>\r
+<p>\r
+ Loads the tpl from the file descriptor given in the following argument.\r
+ The descriptor is read until one complete tpl image is loaded; no bytes\r
+ past the end of the tpl image will be read. The descriptor can be either\r
+ blocking or non-blocking, but will busy-loop if non-blocking and the\r
+ contents cannot be read immediately.\r
+</p>\r
+</dd>\r
+</dl></div>\r
+<div class="paragraph"><p>During loading, the tpl image will be extensively checked for internal validity.</p></div>\r
+<div class="paragraph"><p>This function returns 0 on success or -1 on error.</p></div>\r
+<h4 id="excess_ok"><tt>TPL_EXCESS_OK</tt></h4>\r
+<div class="paragraph"><p>When reading a tpl image from a file or memory (but not from a file descriptor)\r
+the size of the file or memory buffer must exactly equal that of the tpl image\r
+stored therein. In other words, no excess trailing data beyond the tpl image is\r
+permitted. The bit flag <tt>TPL_EXCESS_OK</tt> can be OR’d with <tt>TPL_MEM</tt> or <tt>TPL_FILE</tt>\r
+to relax this requirement.</p></div>\r
+<div class="paragraph"><p>A situation where this flag can be useful is in conjunction with <tt>tpl_dump</tt> in\r
+the <tt>TPL_MEM|TPL_PREALLOCD</tt> mode. In this example, the program does not concern\r
+itself with the actual tpl size as long as <tt>LEN</tt> is sufficiently large.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>char buf[LEN]; /* will store and read tpl images here */\r
+...\r
+tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buf, LEN);\r
+...\r
+tpl_load(tn, TPL_MEM|TPL_EXCESS_OK, buf, LEN);</tt></pre>\r
+</div></div>\r
+<h3 id="tpl_unpack">tpl_unpack</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The <tt>tpl_unpack()</tt> function unpacks data from the tpl. When data is unpacked,\r
+it is copied to the C program variables originally specified in <tt>tpl_map()</tt>.\r
+The first argument to <tt>tpl_unpack</tt> is the <tt>tpl_node*</tt> for the tpl and the\r
+second argument is an <a href="#index">index number</a>.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tn = tpl_map( "A(i)A(c)", &i, &c );\r
+tpl_load( tn, TPL_FILE, "nested.tpl" );\r
+while (tpl_unpack( tn, 1) > 0) printf("i is %d\n", i); /* unpack index 1 */\r
+while (tpl_unpack( tn, 2) > 0) printf("c is %c\n", c); /* unpack index 2 */</tt></pre>\r
+</div></div>\r
+<h4 id="_index_number_0_2">Index number 0</h4>\r
+<div class="paragraph"><p>It is necessary to unpack index number 0 only if the format string contains\r
+characters that are not inside an <tt>A(…)</tt>, such as the <tt>i</tt> in the format string\r
+<tt>iA(c)</tt>.</p></div>\r
+<h4 id="_variable_length_arrays_2">Variable-length arrays</h4>\r
+<h5 id="_unpacking_elements_from_an_array">Unpacking elements from an array</h5>\r
+<div class="paragraph"><p>For variable-length arrays, each call to <tt>tpl_unpack()</tt> unpacks another element.\r
+The return value can be used to tell when you’re done: if it’s positive, an\r
+element was unpacked; if it’s 0, nothing was unpacked because there are no more\r
+elements. A negative retun value indicates an error (e.g. invalid index number).\r
+In this document, we usually unpack variable-length arrays using a <tt>while</tt> loop:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>while( tpl_unpack( tn, 1 ) > 0 ) {\r
+ /* got another element */\r
+}</tt></pre>\r
+</div></div>\r
+<h5 id="_array_length">Array length</h5>\r
+<div class="paragraph"><p>When unpacking a variable-length array, it may be convenient to know ahead of\r
+time how many elements will need to be unpacked. You can use <tt>tpl_Alen()</tt> to\r
+get this number.</p></div>\r
+<h5 id="nested_unpack">Unpacking nested arrays</h5>\r
+<div class="paragraph"><p>In a format string containing a nested variable-length array such as <tt>A(A(s))</tt>,\r
+unpack the outer, parent array before unpacking the child array.</p></div>\r
+<div class="paragraph"><p>When you unpack a parent array, it prepares the child array for unpacking.\r
+After unpacking the elements of the child array, the program can repeat the\r
+process by unpacking another parent element, then the child elements, and so on.\r
+The example below unpacks a tpl having the format string <tt>A(A(c))</tt>.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Unpacking nested arrays</div>\r
+<div class="content">\r
+<pre><tt>#include "tpl.h"\r
+#include <stdio.h>\r
+\r
+int main() {\r
+ char c;\r
+ tpl_node *tn;\r
+\r
+ tn = tpl_map("A(A(c))", &c);\r
+\r
+ tpl_load(tn, TPL_FILE, "test40.tpl");\r
+ while (tpl_unpack(tn,1) > 0) {\r
+ while (tpl_unpack(tn,2) > 0) printf("%c ",c);\r
+ printf("\n");\r
+ }\r
+ tpl_free(tn);\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The file <tt>test40.tpl</tt> is from the <a href="#nested_pack">nested packing example</a>. When\r
+run, this program prints:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>a b\r
+1 2 3</tt></pre>\r
+</div></div>\r
+<h3 id="tpl_free">tpl_free</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>The final step for any tpl is to release it using <tt>tpl_free()</tt>. Its only\r
+argument is the the <tt>tpl_node*</tt> to free.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tpl_free( tn );</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This function does not return a value (it is <tt>void</tt>).</p></div>\r
+<h3 id="tpl_alen">tpl_Alen</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This function takes a <tt>tpl_node*</tt> and an index number and returns an <tt>int</tt>\r
+specifying the number of elements in the variable-length array.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>num_elements = tpl_Alen(tn, index);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This is mainly useful for programs that unpack data and need to know ahead of\r
+time the number of elements that will need to be unpacked. (It returns the\r
+current number of elements; it will decrease as elements are unpacked).</p></div>\r
+<h3 id="tpl_peek">tpl_peek</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This function peeks into a file or a memory buffer containing a tpl image and\r
+and returns a copy of its format string. It can also peek at the lengths of\r
+any fixed-length arrays in the format string, or it can also peek into the data\r
+stored in the tpl.</p></div>\r
+<h4 id="_format_peek">Format peek</h4>\r
+<div class="paragraph"><p>The format string can be obtained\r
+like this:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>fmt = tpl_peek(TPL_FILE, "file.tpl");\r
+fmt = tpl_peek(TPL_MEM, addr, sz);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>On success, a copy of the format string is returned. The caller must eventually\r
+free it. On error, such as a non-existent file, or an invalid tpl image, it\r
+returns <tt>NULL</tt>.</p></div>\r
+<h4 id="_array_length_peek">Array length peek</h4>\r
+<div class="paragraph"><p>The lengths of all fixed-length arrays in the format string can be queried using\r
+the <tt>TPL_FXLENS</tt> mode. It provides the number of such fixed-length arrays and\r
+their lengths. If the former is non-zero, the caller must free the latter array\r
+when finished. The format string itself must also be freed.</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>uint32_t num_fxlens, *fxlens, j;\r
+fmt = tpl_peek(TPL_FILE|TPL_FXLENS, filename, &num_fxlens, &fxlens);\r
+if (fmt) {\r
+ printf("format %s, num_fxlens %u\n", fmt, num_fxlens);\r
+ for(j=0; j<num_fxlens; j++) printf("fxlens[%u] %u\n", j, fxlens[j]);\r
+ if (num_fxlens > 0) free(fxlens);\r
+ free(fmt);\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The <tt>TPL_FXLENS</tt> mode is mutually exclusive with <tt>TPL_DATAPEEK</tt>.</p></div>\r
+<h4 id="_data_peek">Data peek</h4>\r
+<div class="paragraph"><p>To peek into the data, additional arguments are used. This is a quick\r
+alternative to mapping, loading and unpacking the tpl, but peeking is limited\r
+to the data in index 0. In other words, no peeking into <tt>A(…)</tt> types.\r
+Suppose the tpl image in <tt>file.tpl</tt> has the format string <tt>siA(i)</tt>. Then the\r
+index 0 format characters are <tt>si</tt>. This is how to peek at their content:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>char *s;\r
+int i;\r
+fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "si", &s, &i);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Now <tt>s</tt>, <tt>i</tt>, and <tt>fmt</tt> have been populated with data. The caller must\r
+eventually free <tt>fmt</tt> and <tt>s</tt> because they are allocated strings.\r
+Of course, it works with <tt>TPL_MEM</tt> as well as <tt>TPL_FILE</tt>. Notice that\r
+<tt>TPL_DATAPEEK</tt> was OR’d with the mode. You can also specify <em>any leading\r
+portion</em> of the index 0 format if you don’t want to peek at the whole thing:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "s", &s);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The <tt>TPL_DATAPEEK</tt> mode is mutually exclusive with <tt>TPL_FXLENS</tt>.</p></div>\r
+<h5 id="_structure_peek">Structure peek</h5>\r
+<div class="paragraph"><p>Lastly you can peek into <tt>S(…)</tt> structures in index 0, but omit the\r
+surrounding <tt>S(…)</tt> in the format, and specify an argument to receive\r
+each structure member individually. You can specify any leading portion\r
+of the structure format. For example if <tt>struct.tpl</tt> has the format string\r
+<tt>S(si)</tt>, you can peek at its data in these ways:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "s", &s);\r
+fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "si", &s, &i);</tt></pre>\r
+</div></div>\r
+<h3 id="tpl_jot">tpl_jot</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>This is a quick shortcut for generating a tpl. It can be used instead of the\r
+usual "map, pack, dump, and free" lifecycle. With <tt>tpl_jot</tt> all those steps are\r
+handled for you. It only works for simple formats-- namely, those without\r
+<tt>A(…)</tt> in their format string. Here is how it is used:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>char *hello = "hello", *world = "world";\r
+tpl_jot( TPL_FILE, "file.tpl", "ss", &hello, &world);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>It supports the three standard modes, <tt>TPL_FILE</tt>, <tt>TPL_FD</tt> and <tt>TPL_MEM</tt>.\r
+It returns -1 on failure (such as a bad format string or error writing the\r
+file) or 0 on success.</p></div>\r
+<h3 id="hooks">tpl_hook</h3><div style="clear:left"></div>\r
+<div class="paragraph"><p>Most users will just leave these hooks at their default values. You can change\r
+these hook values if you want to modify tpl’s internal memory management and\r
+error reporting behavior.</p></div>\r
+<div class="paragraph"><p>A global structure called <tt>tpl_hook</tt> encapsulates the hooks. A program can\r
+reconfigure any hook by specifying an alternative function whose prototype\r
+matches the default. For example:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>#include "tpl.h"\r
+extern tpl_hook_t tpl_hook;</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>int main() {\r
+ tpl_hook.oops = printf;\r
+ ...\r
+}</tt></pre>\r
+</div></div>\r
+<div class="tableblock">\r
+<table rules="none"\r
+width="90%"\r
+frame="border"\r
+cellspacing="0" cellpadding="4">\r
+<caption class="title">Configurable hooks</caption>\r
+<col width="33%" />\r
+<col width="33%" />\r
+<col width="33%" />\r
+<thead valign="top">\r
+<tr>\r
+<th align="left">Hook </th>\r
+<th align="left">Description </th>\r
+<th align="left"> Default</th>\r
+</tr>\r
+</thead>\r
+<tbody valign="top">\r
+<tr>\r
+<td align="left"><p class="table"><tt>tpl_hook.oops</tt></p></td>\r
+<td align="left"><p class="table">log error messages</p></td>\r
+<td align="left"><p class="table"><tt>tpl_oops</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="left"><p class="table"><tt>tpl_hook.malloc</tt></p></td>\r
+<td align="left"><p class="table">allocate memory</p></td>\r
+<td align="left"><p class="table"><tt>malloc</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="left"><p class="table"><tt>tpl_hook.realloc</tt></p></td>\r
+<td align="left"><p class="table">reallocate memory</p></td>\r
+<td align="left"><p class="table"><tt>realloc</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="left"><p class="table"><tt>tpl_hook.free</tt></p></td>\r
+<td align="left"><p class="table">free memory</p></td>\r
+<td align="left"><p class="table"><tt>free</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="left"><p class="table"><tt>tpl_hook.fatal</tt></p></td>\r
+<td align="left"><p class="table">log fatal message and exit</p></td>\r
+<td align="left"><p class="table"><tt>tpl_fatal</tt></p></td>\r
+</tr>\r
+<tr>\r
+<td align="left"><p class="table"><tt>tpl_hook.gather_max</tt></p></td>\r
+<td align="left"><p class="table">tpl_gather max image size</p></td>\r
+<td align="left"><p class="table"><tt>0 (unlimited)</tt></p></td>\r
+</tr>\r
+</tbody>\r
+</table>\r
+</div>\r
+<h4 id="_the_oops_hook">The oops hook</h4>\r
+<div class="paragraph"><p>The <tt>oops</tt> has the same prototype as <tt>printf</tt>. The built-in default oops\r
+handling function writes the error message to <tt>stderr</tt>.</p></div>\r
+<h4 id="_the_fatal_hook">The fatal hook</h4>\r
+<div class="paragraph"><p>The fatal hook is invoked when a tpl function cannot continue because of an out-\r
+of-memory condition or some other usage violation or inconsistency. It has this\r
+prototype:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>void fatal_fcn(char *fmt, ...);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The <tt>fatal</tt> hook must not return. It must either exit, <em>or</em> if the program needs\r
+to handle the failure and keep executing, <tt>setjmp</tt> and <tt>longjmp</tt> can be used.\r
+The default behavior is to <tt>exit(-1)</tt>.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Using longjmp in a fatal error handler</div>\r
+<div class="content">\r
+<pre><tt>#include <setjmp.h>\r
+#include <stdio.h>\r
+#include <stdarg.h>\r
+#include "tpl.h"\r
+\r
+jmp_buf env;\r
+extern tpl_hook_t tpl_hook;\r
+\r
+void catch_fatal(char *fmt, ...) {\r
+ va_list ap;\r
+\r
+ va_start(ap, fmt);\r
+ vfprintf(stderr, fmt, ap);\r
+ va_end(ap);\r
+ longjmp(env,-1); /* return to setjmp point */\r
+}\r
+\r
+int main() {\r
+ int err;\r
+ tpl_node *tn;\r
+ tpl_hook.fatal = catch_fatal; /* install fatal handler */\r
+\r
+ err = setjmp(env); /* on error, control will return here */\r
+ if (err) {\r
+ printf("caught error!\n");\r
+ return -1;\r
+ }\r
+\r
+ tn = tpl_map("@"); /* generate a fatal error */\r
+ printf("program ending, without error\n");\r
+ return 0;\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This example is included in <tt>tests/test123.c</tt>. When run, this program prints:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>unsupported option @\r
+failed to parse @\r
+caught error!</tt></pre>\r
+</div></div>\r
+<h3 id="_tpl_gather">tpl_gather</h3><div style="clear:left"></div>\r
+<div class="sidebarblock">\r
+<div class="sidebar-content">\r
+<div class="sidebar-title">Most programs don’t need this</div>\r
+<div class="paragraph"><p>Normally, <tt>tpl_load()</tt> is used to read a tpl image having an expected format\r
+string. A more generic operation is to acquire a tpl image whose format string is\r
+unknown. E.g., a generic message-receiving function might gather tpl images of\r
+varying format and route them to their final destination. This is the purpose of\r
+<tt>tpl_gather</tt>. It produces a memory buffer containing one tpl image. If there\r
+are multiple contiguous images in the input, it gathers exactly one image at a\r
+time.</p></div>\r
+</div></div>\r
+<div class="paragraph"><p>The prototype for this function is:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>int tpl_gather( int mode, ...);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The <tt>mode</tt> argument is one of three constants listed below, which must be\r
+followed by the mode-specific required arguments:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>TPL_GATHER_BLOCKING, int fd, void **img, size_t *sz\r
+TPL_GATHER_NONBLOCKING, int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data\r
+TPL_GATHER_MEM, void *addr, size_t sz, tpl_gather_t **gs, tpl_gather_cb *cb, void *data</tt></pre>\r
+</div></div>\r
+<div class="admonitionblock">\r
+<table><tr>\r
+<td class="icon">\r
+<div class="title">Note</div>\r
+</td>\r
+<td class="content">\r
+<div class="title"><tt>tpl_hook.gather_max</tt></div>All modes honor <tt>tpl_hook.gather_max</tt>, specifying the maximum byte size for a\r
+tpl image to be gathered (the default is unlimited, signified by 0). If a source\r
+attempts to send a tpl image larger than this maximum, whatever partial image\r
+has been read will be discarded, and no further reading will take place; in this\r
+case <tt>tpl_gather</tt> will return a negative (error) value to inform the caller that\r
+it should stop gathering from this source, and close the originating file\r
+descriptor if there is one. (The whole idea is to prevent untrusted sources from\r
+sending extremely large tpl images which would consume too much memory.)</td>\r
+</tr></table>\r
+</div>\r
+<h4 id="_tt_tpl_gather_blocking_tt"><tt>TPL_GATHER_BLOCKING</tt></h4>\r
+<div class="paragraph"><p>In this mode, <tt>tpl_gather</tt> blocks while reading file descriptor <tt>fd</tt> until one\r
+complete tpl image is read. No bytes past the end of the tpl image will be read.\r
+The address of the buffer containing the image is returned in <tt>img</tt> and its size\r
+is placed in <tt>sz</tt>. The caller is responsible for eventually freeing the buffer.\r
+The function returns 1 on success, 0 on end-of-file, or a negative number on\r
+error.</p></div>\r
+<h4 id="_tt_tpl_gather_nonblocking_tt"><tt>TPL_GATHER_NONBLOCKING</tt></h4>\r
+<div class="paragraph"><p>This mode is for non-blocking, event-driven programs that implement their\r
+own file descriptor readability testing using <tt>select()</tt> or the like. In this\r
+mode, tpl images are gathered in chunks as data becomes readable. Whenever a\r
+full tpl image has been gathered, it invokes a caller-specified callback to do\r
+something with the image. The arguments are the file descriptor <tt>fd</tt> which the\r
+caller has determined to be readable and which must be in non-blocking mode, a\r
+pointer to a file-descriptor-specific handle which the caller has declared\r
+(explained below); a callback to invoke when a tpl image has been read; and an\r
+opaque pointer that will passed to the callback.</p></div>\r
+<div class="paragraph"><p>For each file descriptor on which <tt>tpl_gather</tt> will be used, the caller must\r
+declare a <tt>tpl_gather_t*</tt> and initialize it to <tt>NULL</tt>. Thereafter it will be\r
+used internally by <tt>tpl_gather</tt> whenever data is readable on the descriptor.</p></div>\r
+<div class="paragraph"><p>The callback will only be invoked whenever <tt>tpl_gather()</tt> has accumulated one\r
+complete tpl image. It must have this prototype:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>int (tpl_gather_cb)(void *img, size_t sz, void *data);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The callback can do anything with the tpl image but it must not free it. It can\r
+be copied if it needs to survive past the callback’s return. The callback should\r
+return 0 under normal circumstances, or a negative number to abort; that is,\r
+returning a negative number causes <tt>tpl_gather</tt> itself to discard any remaining\r
+full or partial tpl images that have been read, and to return a negative number\r
+(-4 in particular) to signal its caller to close the file descriptor.</p></div>\r
+<div class="paragraph"><p>The return value of <tt>tpl_gather()</tt> is negative if an error occured or 0 if a\r
+normal EOF was encountered-- both cases require that the caller close the file\r
+descriptor (and stop monitoring it for readability, obviously). If the return\r
+value is positive, the function succeeded in gathering whatever data was\r
+currently readable, which may have been a partial tpl image, or one or more\r
+complete images.</p></div>\r
+<h5 id="_typical_usage">Typical Usage</h5>\r
+<div class="paragraph"><p>The program will have established a file descriptor in non-blocking mode and\r
+be monitoring it for readability, using <tt>select()</tt>. Whenever it’s readable, the\r
+program calls <tt>tpl_gather()</tt>. In skeletal terms:</p></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>tpl_gather_t *gt=NULL;\r
+int rc;</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>void fd_is_readable(int fd) {\r
+ rc = tpl_gather( TPL_GATHER_NONBLOCKING, fd, &gt, callback, NULL );\r
+ if (rc <= 0) {\r
+ close(fd); /* got eof or fatal */\r
+ stop_watching_fd(fd);\r
+ }\r
+}</tt></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><tt>int callback( void *img, size_t sz, void *data ) {\r
+ printf("got a tpl image\n"); /* do something with img. do not free it. */\r
+ return 0; /* normal (no error) */\r
+}</tt></pre>\r
+</div></div>\r
+<h4 id="_tt_tpl_gather_mem_tt"><tt>TPL_GATHER_MEM</tt></h4>\r
+<div class="paragraph"><p>This mode is identical to <tt>TPL_GATHER_NONBLOCKING</tt> except that it gathers from a\r
+memory buffer instead of from a file descriptor. In other words, if some other\r
+layer of code-- say, a decryption function (that is decrypting fixed-size\r
+blocks) produces tpl fragments one-by-one, this mode can be used to reconstitute\r
+the tpl images and invoke the callback for each one. Its parameters are the same\r
+as for the <tt>TPL_GATHER_NONBLOCKING</tt> mode except that instead of a file\r
+descriptor, it takes a buffer address and size. The return values are also the\r
+same as for <tt>TPL_GATHER_NONBLOCKING</tt> noting of course there is no file\r
+descriptor to close on a non-positive return value.</p></div>\r
+</div>\r
+<div id="footer">\r
+<div id="footer-text">\r
+Version 1.5<br />\r
+Last updated 2010-02-05 04:37:37 EDT\r
+</div>\r
+</div>\r
+</body>\r
+</html>\r
--- /dev/null
+tpl ChangeLog
+=============
+
+Version 1.5 (2010-02-05)
+--------------------------
+* tpl now builds as a DLL under Microsoft Visual Studio! (thanks, degski and Zhang Yafei!)
+* there are now two download options: the http://downloads.sourceforge.net/tpl/libtpl-1.5.tar.bz2[tarball] and the Visual Studio http://downloads.sourceforge.net/tpl/tpl-1.5-vs2008.zip[solution]
+* a crash in `tpl_free` on certain format strings has been fixed (thanks, Eric Rose!)
+* fixed a bug in `tpl_dump` on 64-bit, big-endian platforms
+* changed some pointer casts from `long` to `uintptr_t` since 64-bit Windows has 32-bit longs
+* tpl has been downloaded 4,195 times.
+
+
+Version 1.4 (2009-04-21)
+--------------------------
+* fixed-length arrays can now be multi-dimensional like `i##`
+* fixed-length string arrays like `s#` are now supported
+* nested structures can now be expressed, using the dollar symbol, e.g. `S(ci$(cc))`
+* `tpl_dump` can use a caller-allocated output buffer (`TPL_MEM|TPL_PREALLOCD`)
+* `tpl_load` can tolerate excess space in input buffer (`TPL_MEM|TPL_EXCESS_OK`)
+* implement `TPL_FXLENS` flag for `tpl_peek` to get lengths of fixed-length arrays
+* implement `TPL_GETSIZE` flag for `tpl_dump` to get dump size without dumping
+* fix success return code from `tpl_dump(TPL_FD,...)` (thanks, Max Lapan!)
+* deprecated the wildcard unpacking `S(*)` feature
+
+Version 1.3 (2009-02-10)
+--------------------------
+* added `TPL_DATAPEEK` mode for `tpl_peek`
+* added support for `NULL` strings
+* added support for 16-bit integer types (`j`,`v`)
+* added `tpl_jot`
+* added support for fixed-length arrays of structures `S(...)#`
+* added support for pre-C99 compilers (thanks, Wei Wei!)
+* improved structure alignment calculation (thanks, Wu Yongwei!)
+* added RPM spec file (thanks, Alessandro Ren!)
+* compiles cleanly with `-Wall` and `-pedantic` and with `-O3`
+* made link:license.html[BSD license] terms even more permissive
+* test suite: exit with status zero when all tests pass
+* added PDF user guide
+* added http://troydhanson.wordpress.com/feed/[update news] image:img/rss.png[(RSS)]
+* added http://apps.sourceforge.net/mediawiki/tpl/[tpl wiki]
+
+Version 1.2 (2007-04-27)
+--------------------------
+* Perl API and XML converter support 64-bit types
+
+Version 1.1 (2007-04-25)
+--------------------------
+* support for serializing C structures
+* support for serializing fixed-length arrays
+* MinGW support (thanks, Horea Haitonic!)
+* revised User Guide
+
+Version 1.0 (2006-09-28)
+--------------------------
+* Initial version
+
+// vim: set tw=80 wm=2 nowrap syntax=asciidoc:
+
--- /dev/null
+Compiling libtpl.a and libtpl.so
+--------------------------------
+
+********************************************************************************
+Normally in the top-level directory you simply run:
+
+ ./configure
+ make
+ make install
+
+The rest of the document is not needed if you use this method.
+********************************************************************************
+
+
+Manual compilation of static and shared library on a GNU Linux system:
+----------------------------------------------------------------------
+
+First cd into the "src" directory.
+
+Static library
+~~~~~~~~~~~~~~
+You can build the static library `libtpl.a` using these commands:
+
+ cc -c tpl.c
+ ar rc libtpl.a tpl.o
+ ranlib libtpl.a
+
+Dynamic library
+~~~~~~~~~~~~~~
+You can build the dynamic library `libtpl.so` using these commands:
+
+ cc -fpic -c tpl.c
+ cc -shared -o libtpl.so tpl.o
+
+Keep in mind that you need to set the `LD_LIBRARY_PATH` environment variable
+to include the directory where `libtpl.so` is installed in order for your
+program to run. Alternatively you can put `libtpl.so` in a standard place like
+`/usr/lib` and regenerate `ld.so.cache` using `ldconfig`.
--- /dev/null
+tpl examples
+============
+Troy D. Hanson <troydhanson@comcast.net>>
+v1.0, October 2006
+
+include::sflogo.txt[]
+include::topnav.txt[]
+
+Examples
+--------
+include::toc.txt[]
+
+This document is a set of representative examples demonstrating how to use
+tpl. If you're looking for a more explanatory document, please read the
+link:userguide.html[User Guide].
+
+An integer array
+~~~~~~~~~~~~~~~~
+
+.Storing an array of integers to file
+-------------------------------------------------------------------------------
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map( "A(i)", &i );
+ for( i=0; i<10; i++ ) {
+ tpl_pack( tn, 1 );
+ }
+ tpl_dump( tn, TPL_FILE, "demo.tpl" );
+ tpl_free( tn );
+}
+-------------------------------------------------------------------------------
+
+A program that unpacks this tpl data file is shown below.
+
+.Re-reading an array of integers from file
+-------------------------------------------------------------------------------
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map( "A(i)", &i );
+ tpl_load( tn, TPL_FILE, "demo.tpl" );
+ while (tpl_unpack( tn, 1 ) > 0) {
+ printf("%d ", i);
+ }
+ tpl_free( tn );
+}
+-------------------------------------------------------------------------------
+
+When run, this program prints:
+
+ 0 1 2 3 4 5 6 7 8 9
+
+
+A nested array
+~~~~~~~~~~~~~~
+
+.Packing nested arrays
+--------------------------------------------------------------------------------
+#include "tpl.h"
+
+int main() {
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(A(c))", &c);
+
+ for(c='a'; c<'c'; c++) tpl_pack(tn,2);
+ tpl_pack(tn, 1);
+
+ for(c='1'; c<'4'; c++) tpl_pack(tn,2);
+ tpl_pack(tn, 1);
+
+ tpl_dump(tn, TPL_FILE, "test40.tpl");
+ tpl_free(tn);
+}
+--------------------------------------------------------------------------------
+
+This creates a nested array in which the parent has two elements: the first
+element is the two-element nested array 'a', 'b'; and the second element is
+the three-element nested array '1', '2', '3'.
+
+.Unpacking nested arrays
+--------------------------------------------------------------------------------
+#include "tpl.h"
+#include <stdio.h>
+
+int main() {
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(A(c))", &c);
+
+ tpl_load(tn, TPL_FILE, "test40.tpl");
+ while (tpl_unpack(tn,1) > 0) {
+ while (tpl_unpack(tn,2) > 0) printf("%c ",c);
+ printf("\n");
+ }
+ tpl_free(tn);
+}
+--------------------------------------------------------------------------------
+
+
+When run, this program prints:
+
+ a b
+ 1 2 3
+
+A string array
+~~~~~~~~~~~~~~
+
+.Packing a string array
+-------------------------------------------------------------------------------
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ char *s;
+
+ tn = tpl_map( "A(s)", &s );
+
+ s = "bob";
+ tpl_pack(tn, 1);
+
+ s = "betty";
+ tpl_pack(tn, 1);
+
+ tpl_dump(tn, TPL_FILE, "strings.tpl");
+ tpl_free(tn);
+ }
+-------------------------------------------------------------------------------
+
+.Unpacking a string array
+-------------------------------------------------------------------------------
+ #include <stdio.h>
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ char *s;
+
+ tn = tpl_map( "A(s)", &s );
+ tpl_load( tn, TPL_FILE, "strings.tpl" );
+
+ while (tpl_unpack( tn, 1 ) > 0) {
+ printf("%s\n", s);
+ free(s); /* important! */
+ }
+
+ tpl_free(tn);
+ }
+-------------------------------------------------------------------------------
+
+When run, this program prints:
+
+ bob
+ betty
+
+Integer/string pairs
+~~~~~~~~~~~~~~~~~~~~
+
+.Packing integer/string pairs
+-------------------------------------------------------------------------------
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ int id;
+ char *name, *names[] = { "joe", "bob", "mary" };
+
+ tn = tpl_map("A(is)", &id, &name);
+
+ for(id=0,name=names[id]; id < 3; name=names[++id])
+ tpl_pack(tn,1);
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test35.tpl");
+ tpl_free(tn);
+}
+-------------------------------------------------------------------------------
+
+.Unpacking integer/string pairs
+-------------------------------------------------------------------------------
+#include <stdio.h>
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ int id;
+ char *name;
+
+ tn = tpl_map("A(is)", &id, &name);
+ tpl_load(tn, TPL_FILE, "/tmp/test35.tpl");
+
+ while ( tpl_unpack(tn,1) > 0 )
+ printf("id %d, user %s\n", id, name);
+
+ tpl_free(tn);
+}
+-------------------------------------------------------------------------------
+
+When run, this program prints:
+
+ id 0, user joe
+ id 1, user bob
+ id 2, user mary
+
+A binary buffer
+~~~~~~~~~~~~~~~
+
+.Packing a binary buffer
+-------------------------------------------------------------------------------
+ #include "tpl.h"
+ #include <sys/time.h>
+
+ int main() {
+ tpl_node *tn;
+ tpl_bin tb;
+ struct timeval tv; /* we'll pack this structure as raw binary */
+ gettimeofday(&tv,NULL); /* populate the structure with some data */
+
+ tn = tpl_map( "B", &tb );
+ tb.sz = sizeof(struct timeval);
+ tb.addr = &tv;
+ tpl_pack( tn, 0 );
+
+ tpl_dump(tn, TPL_FILE, "bin.tpl");
+ tpl_free(tn);
+ }
+-------------------------------------------------------------------------------
+
+
+.Unpacking a binary buffer
+-------------------------------------------------------------------------------
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ tpl_bin tb;
+
+ tn = tpl_map( "B", &tb );
+ tpl_load( tn, TPL_FILE, "bin.tpl" );
+
+ tpl_unpack( tn, 0 );
+ printf("binary buffer of length %d at address %p\n", tb.sz, tb.addr);
+ free(tb.addr); /* important! */
+
+ tpl_free(tn);
+ }
+-------------------------------------------------------------------------------
+
+
+Simple pipe IPC
+~~~~~~~~~~~~~~~
+
+This is a simple example of inter-process communication (IPC) over a pipe.
+
+.IPC over a pipe
+-------------------------------------------------------------------------------
+int main() {
+ tpl_node *tn;
+ unsigned i, sum=0;
+ int fd[2], pid;
+
+ pipe(fd);
+ if ( (pid = fork()) == 0) { /* child */
+
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_FD, fd[0]);
+ while (tpl_unpack(tn,1) > 0) sum += i;
+ tpl_free(tn);
+ printf("sum is %d\n", sum);
+
+ } else if (pid > 0) { /* parent */
+
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10000;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FD, fd[1] );
+ tpl_free(tn);
+
+ waitpid(pid,NULL,0);
+ }
+}
+-------------------------------------------------------------------------------
+
+The child unpacks the integers in the message, and sums them, printing:
+
+ 49995000
+
+The example above (with `#include` headers omitted here) is included in the
+file `tests/test28.c`.
--- /dev/null
+For future reference these are some API design ideas- not working code!
+
+--------------------------------------------------------------------------------
+Java API ideas
+--------------------------------------------------------------------------------
+http://www.ioplex.com/~miallen/encdec/ (binary pack/unpack utilities)
+
+The Java API would take an object, and a list of field names, then use the
+Java Reflection API to read or write those fields during packing and
+unpacking.
+
+I.e. if you are going to unpack a tpl with format string A(if), you
+might create a Java class that has two instance variables (an int and
+a double). Then you create an object of that class, and pass it to
+tpl_map (or perhaps a constructor for the Tpl class), like
+
+class Unpacker {
+ int count;
+ double weight;
+}
+...
+Unpacker up = new Unpacker();
+Tpl tn = new Tpl("A(if)", up, "count", "weight");
+tn.tpl_unpack(1); // stores unpacked values into count,weight using Reflection
+
+--------------------------------------------------------------------------------
+Ruby API ideas
+--------------------------------------------------------------------------------
+#!/usr/local/ruby/bin/ruby -w
+
+class Tpl < Hash
+ def initialize(fmt, *args)
+ @fmt = fmt
+ @args = args
+ end
+
+ def pack
+ @args.each {|key| puts "#{key} #{self[key]}"}
+ end
+end
+
+
+p = Tpl.new("A(i)", :id);
+10.times do |i|
+ p[:id] = i
+ p.pack
+end
+p.dump("/tmp/file.tpl") # p.dump(arg) checks arg.respond_to?(:write)
+
+p = Tpl.new("A(i)", :id);
+p.load("/tmp/file.tpl")
+p.unpack(1) {|h| puts h[:id]}
--- /dev/null
+tpl Perl API
+============
+Troy D. Hanson <troydhanson@comcast.net>
+v1.1, April 2007
+
+include::sflogo.txt[]
+include::topnav.txt[]
+
+Perl API
+--------
+include::toc.txt[]
+
+The Perl API for reading and writing tpl is nearly identical to the C API. This
+document will briefly explain the Perl API and provide examples. The chief
+motivation for having a Perl API is to communicate with C programs that use tpl.
+
+[TIP]
+.Start with the C API
+This document assumes familiarity with the C API. The concepts of using tpl
+are not explained here. For an introduction to tpl and its C API, see the
+link:userguide.html[User Guide].
+
+Tpl.pm
+~~~~~~
+The `Tpl.pm` file (in the `lang/perl`) directory contains the Perl module. You
+can copy it to another directory if you wish. Your Perl program may need to
+include a `use lib` statement to find the module.
+
+ #!/usr/bin/perl
+ use lib "/some/directory";
+ use Tpl;
+
+tpl_map
+~~~~~~~
+This function resembles the C version, except that it's invoked via the `Tpl`
+module, and it takes references to Perl variables after the format string.
+
+ my $i;
+ my $tpl = Tpl->tpl_map("A(i)",\$i);
+
+The return value is a tpl object; all other API calls are object methods.
+Incidentally, there is no `tpl_free()` method corresponding to the C API.
+
+Fixed-length arrays
+^^^^^^^^^^^^^^^^^^^
+Format strings such as `i#` denote a fixed-length array. In the Perl API,
+fixed-length arrays require two arguments: a list reference, and the fixed
+length. For example:
+
+ my @x;
+ my $tpl = Tpl->tpl_map("i#", \@x, 10);
+
+When fixed-length arrays are packed or unpacked, the specified number of
+elements will be copied from (or placed into) the designated list.
+
+Structures
+^^^^^^^^^^
+Format strings containing `S(...)` are handled in the Perl API as if only the
+interior, parenthesized part was present. (It does not work like the C API). So
+simply ignore the `S(...)` and consider only its interior format characters when
+constructing the argument list:
+
+ my ($str, $int);
+ my $tpl = Tpl->tpl_map("S(si)", \$str, \$int);
+
+It really only makes sense to use `S(...)` in a format string in the Perl API if
+you are communicating with a C program that uses structures.
+
+tpl_pack
+~~~~~~~~
+This is nearly identical to the C version. The only argument is the index
+number to pack.
+
+ $tpl->tpl_pack(1);
+
+tpl_dump
+~~~~~~~~
+This method is a little different than the C version. Given no arguments, it
+returns the tpl image; given one argument it writes a file with that name.
+
+ $tpl->tpl_dump("demo.tpl"); # writes demo.tpl
+
+Or,
+
+ my $img = $tpl->tpl_dump();
+
+The tpl image is a binary buffer. You can do whatever you want with it, such as
+write it to a socket or pipe (probably to C program listening on the other end),
+or save it somewhere and later re-load it using `tpl_load()`.
+
+tpl_load
+~~~~~~~~
+This method loads a tpl image from a file or from a Perl variable. It takes
+one argument. If it's not a reference, it's assumed to be a filename to load.
+
+ $tpl->tpl_load("demo.tpl");
+
+Otherwise, if the argument is a Perl reference, it's construed as a variable
+containing the tpl image:
+
+ $tpl->tpl_load(\$img);
+
+The method will `die` if the image is invalid or the file doesn't exist. You
+can wrap it with `eval` to catch such errors:
+
+ eval { $tpl->tpl_load(\$img); };
+ print "failed to load\n" if $@;
+
+tpl_unpack
+~~~~~~~~~~
+This is nearly identical to the C version. The only argument is the index
+number to unpack.
+
+ $tpl->tpl_unpack(1);
+
+Examples
+--------
+
+Integer array
+~~~~~~~~~~~~~
+
+.Packing A(i) to file
+--------------------------------------------------------------------------------
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+for($i=0; $i<10; $i++) {
+ $tpl->tpl_pack(1);
+}
+$tpl->tpl_dump("demo.tpl");
+--------------------------------------------------------------------------------
+
+.Unpacking A(i) from file
+--------------------------------------------------------------------------------
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Tpl;
+
+my $j;
+my $tpl2 = Tpl->tpl_map("A(i)",\$j);
+$tpl2->tpl_load("demo.tpl");
+while($tpl2->tpl_unpack(1) > 0) {
+ print "$j\n";
+}
+--------------------------------------------------------------------------------
+
+Message-passing
+~~~~~~~~~~~~~~~
+While the bulk of this example is socket handling, it demonstrates how you can
+use tpl as a message-passing format. In the real-world, you might have a C
+server and a Perl client, for example. In this example, we'll code both a client
+and a server in Perl.
+
+.A server that sums integers
+********************************************************************************
+Programming literature is rife with contrived examples so we will follow in that
+tradition. Our server will do no more than sum a list of integers. But in doing
+so it will demonstrate message passing adequately. Both its input (the integer
+array) and its output (an integer) are tpl images, passed over a TCP/IP socket.
+********************************************************************************
+
+Server
+^^^^^^
+The server waits for a connection from a client. When it gets one, it accepts
+the connection and immediately forks a child process to handle it. Then it goes
+back to waiting for another new connection.
+
+The server child process handles the client by loading and unpacking the tpl
+image sent by the client (containing an array of integers). It calculates their
+sum and constructs a new tpl image containing the sum, which it sends back to
+the client.
+
+.Server
+--------------------------------------------------------------------------------
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use Tpl;
+
+$SIG{CHLD} = "IGNORE"; # don't create zombies
+
+our $port = 2000;
+
+sub handle_client {
+ my $client = shift;
+
+ undef $/;
+ my $request = <$client>; # get request (slurp)
+
+ # read input array, and calculate total
+ my ($i,$total);
+ my $tpl = Tpl->tpl_map("A(i)", \$i);
+ eval { $tpl->tpl_load(\$request); };
+ die "received invalid tpl" if $@;
+ $total += $i while $tpl->tpl_unpack(1) > 0;
+
+ # formulate response and send
+ my $tpl2 = Tpl->tpl_map("i", \$total);
+ $tpl2->tpl_pack(0);
+ my $response = $tpl2->tpl_dump();
+ print $client $response;
+ close $client;
+}
+
+my $server = IO::Socket::INET->new(LocalPort => $port,
+ Type => SOCK_STREAM,
+ Reuse => 1,
+ Listen => 10 )
+ or die "Can't listen on port $port: $!\n";
+
+while (1) {
+ my $client = $server->accept();
+ next unless $client;
+ # new connection
+ my $pid = fork;
+ die "can't fork: $!\n" unless defined $pid;
+ if ($pid > 0) {
+ # parent
+ close $client;
+ } elsif ($pid == 0) {
+ # child
+ handle_client($client);
+ exit(0);
+ }
+}
+close ($server);
+--------------------------------------------------------------------------------
+
+Client
+^^^^^^
+
+The client is a simpler program. It constructs the tpl image containing the
+integer array (taken from its command-line arguments), connects to the server
+and sends the tpl image to it, and then awaits the response tpl. The response
+containing the sum is loaded, unpacked and printed.
+
+.Client
+--------------------------------------------------------------------------------
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use Tpl;
+
+our $port = 2000;
+
+# construct tpl
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+$tpl->tpl_pack(1) while ($i=shift @ARGV);
+my $request = $tpl->tpl_dump();
+
+# send to server, get response
+my $socket = IO::Socket::INET->new("localhost:$port") or die "can't connect";
+print $socket $request;
+shutdown($socket,1); # done writing (half-close)
+undef $/;
+my $response = <$socket>; # get reply (slurp)
+
+# decode response (or print error)
+my $total;
+my $tpl2 = Tpl->tpl_map("i", \$total);
+eval { $tpl2->tpl_load(\$response); };
+die "invalid response\n" if $@;
+$tpl2->tpl_unpack(0);
+print "total is $total\n";
+--------------------------------------------------------------------------------
+
+Running thise example
+^^^^^^^^^^^^^^^^^^^^^
+If the client and server programs are in `client.pl` and `server.pl`, then
+you can run the example by starting the server in one window:
+
+ ./server.pl
+
+Then run the client in another window. E.g.,
+
+ ./client.pl 1 2 3 4 5
+
+The client runs and then exits, printing:
+
+ total is 15
+
+You can re-run the client with different arguments. When done, type `Ctrl-C` in
+the server window to terminate it.
+
+// vim: set tw=80 wm=2 syntax=asciidoc:
+
--- /dev/null
+ifdef::backend-xhtml11[]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+<a style="float: right;" href="http://sourceforge.net/projects/tpl"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=157637&type=16" width="150" height="40" alt="SourceForge.net" /></a>
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+endif::backend-xhtml11[]
--- /dev/null
+ifdef::backend-xhtml11[]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+<div id="toc"></div>
+<script>
+window.onload=generate_TOC
+
+/* Author: Mihai Bazon, September 2002
+ * http://students.infoiasi.ro/~mishoo
+ *
+ * Table Of Content generator
+ * Version: 0.4
+ *
+ * Feel free to use this script under the terms of the GNU General Public
+ * License, as long as you do not remove or alter this notice.
+ */
+
+ /* modified by Troy D. Hanson, September 2006. License: GPL */
+
+function H_getText(el) {
+ var text = "";
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {
+ if (i.nodeType == 3 /* Node.TEXT_NODE, IE doesn't speak constants */)
+ text += i.data;
+ else if (i.firstChild != null)
+ text += H_getText(i);
+ }
+ return text;
+}
+
+function TOC_EL(el, text, level) {
+ this.element = el;
+ this.text = text;
+ this.level = level;
+}
+
+function getHeadlines(el) {
+ var l = new Array;
+ var rx = /[hH]([2-3])/;
+ // internal recursive function that scans the DOM tree
+ var rec = function (el) {
+ for (var i = el.firstChild; i != null; i = i.nextSibling) {
+ if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
+ if (rx.exec(i.tagName))
+ l[l.length] = new TOC_EL(i, H_getText(i), parseInt(RegExp.$1));
+ rec(i);
+ }
+ }
+ }
+ rec(el);
+ return l;
+}
+
+function generate_TOC() {
+ var parent = document.getElementById("toc");
+ var toc_hdr = document.createElement("div");
+ var toc_hdr_txt = document.createTextNode("CONTENTS");
+ toc_hdr.appendChild(toc_hdr_txt);
+ /* toc_hdr.setAttribute("id","hdr"); */
+ toc_hdr.id = "hdr";
+ parent.appendChild(toc_hdr);
+ var hs = getHeadlines(document.getElementsByTagName("body")[0]);
+ for (var i = 0; i < hs.length; ++i) {
+ var hi = hs[i];
+ var d = document.createElement("div");
+ if (hi.element.id == "") hi.element.id = "gen" + i;
+ var a = document.createElement("a");
+ a.href = "#" + hi.element.id;
+ a.appendChild(document.createTextNode(hi.text));
+ d.appendChild(a);
+ d.className = "level" + hi.level;
+ parent.appendChild(d);
+ /*
+ if (hi.level == 3) {
+ var dvtop = document.createElement("div");
+ dvtop.className = "toplink";
+ dvtop.appendChild(document.createTextNode("^top^"));
+ dvtop.onclick=function(){scrollTo(0,0);};
+ hi.element.appendChild(dvtop);
+ }
+ */
+ }
+}
+</script>
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+endif::backend-xhtml11[]
--- /dev/null
+ifdef::backend-xhtml11[]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ <div id="topnav" style="font-size: 9pt; font-family: sans-serif;">
+ <a style="padding: 8px;" href="http://sourceforge.net/projects/tpl/">sf.net summary page</a> >
+ <a style="padding: 8px;" href="index.html">tpl home</a> >
+ {doctitle}
+ <a style="padding: 8px;" href="userguide.pdf">[View PDF]</a>
+ </div>
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+endif::backend-xhtml11[]
--- /dev/null
+tpl User Guide
+==============
+Troy D. Hanson <thanson@users.sourceforge.net>
+v1.5, February 2010
+
+include::sflogo.txt[]
+include::topnav.txt[]
+
+Overview
+--------
+include::toc.txt[]
+
+Serialization in C
+~~~~~~~~~~~~~~~~~~
+Tpl is a library for serializing C data. The data is stored in its natural
+binary form. The API is small and tries to stay "out of the way".
+Tpl can serialize many C data types, including structures.
+
+Uses for tpl
+~~~~~~~~~~~~
+Tpl makes a convenient file format. For example, suppose a program needs to
+store a list of user names and ids. This can be expressed using the format
+string `A(si)`. If the program needs two such lists (say, one for regular
+users and one for administrators) this could be expressed as `A(si)A(si)`. It
+is easy to read and write this kind of structured data using tpl.
+
+Tpl can also be used as an IPC message format. It handles byte order issues
+and deframing individual messages off of a stream automatically.
+
+Expressing type
+~~~~~~~~~~~~~~~
+The "data type" of a tpl is explicitly stated as a format string. There is
+never any ambiguity about the type of data stored in a tpl. Some examples:
+
+* `A(is)` is a variable-length array of integer-string pairs
+* `A(is)A(is)` are two such arrays, completely independent of one another
+* `S(ci)` is a structure containing a char and integer
+* `S(ci)#` is a fixed-length array of the latter structure
+* `A(A(i))` is a nested array, that is, an array of integer arrays
+
+The tpl image
+~~~~~~~~~~~~~
+A tpl image is the serialized form of a tpl, stored in a memory buffer or file,
+or written to a file descriptor.
+
+What's in a tpl image?
+^^^^^^^^^^^^^^^^^^^^^^
+There is no need to understand the internal structure of the tpl image. But for the
+curious, the image is a strictly defined binary buffer having two sections,
+a header and the data. The header encodes the length of the image, its
+format string, endian order and other flags. The data section contains the
+packed data.
+
+No framing needed
+^^^^^^^^^^^^^^^^^
+A property of the tpl image is that consecutive images can be written to a stream
+without requiring any delimiter between them. The reader making use of
+`tpl_gather` (or `tpl_load` in `TPL_FD` mode) will obtain exactly one tpl image at
+a time. Therefore tpl images can be used as an IPC message format without any
+higher-level framing protocol.
+
+Data portability
+^^^^^^^^^^^^^^^^
+A tpl image generated on one kind of CPU will generally be portable to other
+CPU types when tpl is used properly. This may be a surprise considering that
+tpl is a binary format. But tpl has been carefully designed to make this work.
+Each <<types,format character>> has an associated explicitly-sized type. For
+integer and floating point types, whose "endian" or byte-order convention varies
+from one CPU to another, tpl automatically and transparently corrects the
+endian order (if needed) during the unpacking process. Floating point numbers
+present their own <<trouble_with_double,special difficulties>>. 'No guarantees
+are made with regard to floating point portability.' That said, because many
+modern CPU's use IEEE 754 floating point representation, data is likely to be
+portable among them.
+
+XML and Perl
+~~~~~~~~~~~~
+'Note: The `tplxml` utility and the Perl module are currently unsupported in tpl 1.5.'
+
+XML
+^^^
+While a tpl image is a binary entity, you can view any tpl image in XML format
+using the included `tplxml` utility, located in the `lang/perl` directory.
+
+ tplxml file.tpl > file.xml
+ tplxml file.xml > file.tpl
+
+The utility is bidirectional, as shown. The file extension is not important;
+`tplxml` inspects its input to see if it's tpl or XML. You can also pipe data
+into it instead of giving it a filename. The `tplxml` utility is slow. Its
+purpose is two-fold: debugging (manual inspection of the data in a tpl), and
+interoperability with XML-based programs. The resulting XML is often ten times
+the size of the original binary tpl image.
+
+Perl
+^^^^
+There is a Perl module in `lang/perl/Tpl.pm`. The link:perl.html[Perl API]
+is convenient for writing Perl scripts that interoperate with C programs, and
+need to pass structured data back and forth. It is written in pure Perl.
+
+Platforms
+~~~~~~~~~
+The tpl software was developed for POSIX systems and has been tested on 32- and 64-bit
+platforms including:
+
+ * Linux
+ * Solaris
+ * Mac OS X
+ * OpenBSD
+ * Windows using Visual Studio 2008 or 2010, or Cygwin or MinGW
+
+BSD licensed
+~~~~~~~~~~~~
+This software is made available under the
+link:license.html[revised BSD license].
+It is free and open source.
+
+Download
+~~~~~~~~
+Please follow the link to download on the
+http://tpl.sourceforge.net[tpl website].
+
+Getting help
+~~~~~~~~~~~~
+If you need help, you are welcome to email the author at
+mailto:thanson@users.sourceforge.net[].
+
+Resources
+~~~~~~~~~
+News::
+ The author has a news feed for http://troydhanson.wordpress.com/feed/[software updates] image:img/rss.png[(RSS)].
+
+Build and install
+-----------------
+
+Tpl has no dependencies on libraries other than the system C library. You
+can simply copy the tpl source into your project, so you have no dependencies.
+Alternatively, you can build tpl as a library and link it to your program.
+
+As source
+~~~~~~~~~
+The simplest way to use tpl is to copy the source files `tpl.h` and `tpl.c`
+(from the `src/` directory) right into your project, and build them with the
+rest of your source files. No special compiler flags are required.
+
+As a library
+~~~~~~~~~~~~
+Alternatively, to build tpl as a library, from the top-level directory, run:
+
+ ./configure
+ make
+ make install
+
+This installs a static library `libtpl.a` and a shared library (e.g.,
+`libtpl.so`), if your system supports them, in standard places. The installation
+directory can be customized using `./configure --prefix=/some/directory`. Run
+`configure --help` for further options.
+
+Test suite
+^^^^^^^^^^
+You can compile and run the built-in test suite by running:
+
+ cd tests/
+ make
+
+On Windows
+~~~~~~~~~~
+
+DLL
+^^^
+On the tpl home page, a Visual Studio 2008 solution package is available for
+download. This zip file contains pre-built 32- and 64-bit versions of tpl as a
+DLL. If you like, you can build the DLL yourself using VS2008 or VS2010 (the
+free Express Edition is sufficient) by opening the solution file and choosing
+Build Solution.
+
+Non-DLL usage
+^^^^^^^^^^^^^
+Alternatively, tpl can be used directly (instead of as a DLL) by compiling
+the tpl sources right into your program. To do this, add `tpl.c`, `tpl.h`,
+`win/mman.h` and `win/mmap.c` to your program's source and header files and
+add the preprocessor definition `TPL_NOLIB`.
+
+MinGW/Cygwin
+^^^^^^^^^^^^
+Prior to tpl release 1.5, using tpl on Windows required building it with MinGW
+or Cygwin. This is no longer necessary. If you want to build it that way anyway,
+use the non-Windows (i.e. tar.bz2) tpl download and follow the "configure; make;
+make install" approach.
+
+API concepts
+------------
+To use tpl, you need to know the order in which to call the API functions, and
+the background concepts of format string, arrays and index numbers.
+
+Order of functions
+~~~~~~~~~~~~~~~~~~
+Creating a tpl is always the first step, and freeing it is the last step. In
+between, you either pack and dump the tpl (if you're serializing data) or you
+load a tpl image and unpack it (if you're deserializing data).
+
+.Order of usage
+[width="50%",cols="^1,^5m,^5m",grid="none",options="header"]
+|===============================================================================
+|Step | If you're serializing...| If you're deserializing...
+| 1. | tpl_map() | tpl_map()
+| 2. | tpl_pack() | tpl_load()
+| 3. | tpl_dump() | tpl_unpack()
+| 4. | tpl_free() | tpl_free()
+|===============================================================================
+
+[[format]]
+Format string
+~~~~~~~~~~~~~
+When a tpl is created using `tpl_map()`, its data type is expressed as a format
+string. Each character in the format string has an associated argument of a
+specific type. For example, this is how a format string and its arguments are
+passed in to `tpl_map`:
+
+ tpl_node *tn;
+ char c;
+ int i[10];
+ tn = tpl_map("ci#", &c, i, 10); /* ci# is our format string */
+
+[[types]]
+.Supported format characters
+[width="90%",grid="none",options="header",cols="5^m,20,20"]
+|================================================================================
+|Type | Description | Required argument type
+| j | 16-bit signed int | int16_t* or equivalent
+| v | 16-bit unsigned int | uint16_t* or equivalent
+| i | 32-bit signed int | int32_t* or equivalent
+| u | 32-bit unsigned int | uint32_t* or equivalent
+| I | 64-bit signed int | int64_t* or equivalent
+| U | 64-bit unsigned int | uint64_t* or equivalent
+| c | character (byte) | char*
+| s | string | char**
+| f | 64-bit double precision float | double* (varies by platform)
+| # | array length; modifies preceding `iujvIUcsf` or `S(...)`| int
+| B | binary buffer (arbitrary-length) | tpl_bin*
+| S | structure (...) | struct *
+| $ | nested structure (...) | none
+| A | array (...) | none
+|================================================================================
+
+Explicit sizes
+^^^^^^^^^^^^^^
+The sizes of data types such as `long` and `double` vary by platform. This must
+be kept in mind because most tpl format characters require a pointer argument to
+a specific-sized type, listed above. You can use explicit-sized types such as
+`int32_t` (defined in `inttypes.h`) in your program if you find this helpful.
+
+[[trouble_with_double]]
+The trouble with double
++++++++++++++++++++++++
+Unfortunately there are no standard explicit-sized floating-point types-- no
+`float64_t`, for example. If you plan to serialize `double` on your platform
+using tpl's `f` format character, first be sure that your `double` is 64 bits.
+Second, if you plan to deserialize it on a different kind of CPU, be sure that
+both CPU's use the same floating-point representation such as IEEE 754.
+
+[[arrays]]
+Arrays
+~~~~~~
+Arrays come in two kinds: *fixed-length* and *variable-length* arrays.
+Intuitively, they can be thought of like conventional C arrays and linked lists.
+In general, use fixed-length arrays if possible, and variable-length arrays
+if necessary. The variable-length arrays support more complex data types, and
+give or receive the elements to your program one by one.
+
+Fixed-length vs. Variable-length arrays
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Notation::
+ Fixed-length arrays are denoted like `i#` (a simple type followed by one or
+ more `#` signs), but variable-length arrays are denoted like `A(i)`.
+Element handling::
+ All the elements of a fixed-length array are packed or unpacked at once. But
+ the elements of a variable-length array are packed or unpacked one by one.
+Array length::
+ The number of elements in a fixed-length array is specified before use--
+ before any data is packed. But variable-length arrays do not have a fixed
+ element count. They can have any number of elements packed into them. When
+ unpacking a variable-length array, they are unpacked one by one until they
+ are exhausted.
+Element types::
+ Elements of fixed-length arrays can be the integer, byte, double, string
+ types or structures. (This excludes format characters `BA`). Fixed-length
+ arrays can also be multi-dimensional like `i##`. Variable-length arrays can
+ have simple or complex elements-- for example, an array of ints `A(i)`, an
+ array of int/double pairs `A(if)`, or even nested arrays like `A(A(if))`.
+
+Before explaining all the concepts, it's illustrative to see how both kinds of
+arrays are used. Let's pack the integers 0 through 9 both ways.
+
+//|================================================================================
+//|Fixed-length array packing | Variable-length array packing
+//|#include "tpl.h" | #include "tpl.h"
+//|int main() { | int main() {
+//| tpl_node *tn; | tpl_node *tn;
+//| int x[] = {0,1,2,3,4,5,6,7,8,9}; | int x;
+//| |
+//| tn = tpl_map("i#", x, 10); | tn = tpl_map("A(i)", &x);
+//| /* pack all 10 elements at once */ | /* pack one element at a time */
+//| tpl_pack(tn,0); | for(x = 0; x < 10; x++) tpl_pack(tn,1);
+//| tpl_dump(tn, TPL_FILE, "/tmp/fixed.tpl");| tpl_dump(tn, TPL_FILE, "/tmp/variable.tpl");
+//| tpl_free(tn); | tpl_free(tn);
+//|} | }
+//|================================================================================
+
+[[fixed_pack]]
+.Packing 0-9 as a fixed-length array
+-------------------------------------------------------------------------------
+#include "tpl.h"
+int main() {
+ tpl_node *tn;
+ int x[] = {0,1,2,3,4,5,6,7,8,9};
+
+ tn = tpl_map("i#", x, 10);
+ tpl_pack(tn,0); /* pack all 10 elements at once */
+ tpl_dump(tn, TPL_FILE, "/tmp/fixed.tpl");
+ tpl_free(tn);
+}
+-------------------------------------------------------------------------------
+
+Note that the length of the fixed-length array (10) was passed as an argument to
+`tpl_map()`. The corresponding unpacking <<fixed_unpack,example>> is listed
+further below. Now let's see how we would pack 0-9 as a variable-length array:
+
+.Packing 0-9 as a variable-length array
+-------------------------------------------------------------------------------
+#include "tpl.h"
+int main() {
+ tpl_node *tn;
+ int x;
+
+ tn = tpl_map("A(i)", &x);
+ for(x = 0; x < 10; x++) tpl_pack(tn,1); /* pack one element at a time */
+ tpl_dump(tn, TPL_FILE, "/tmp/variable.tpl");
+ tpl_free(tn);
+}
+-------------------------------------------------------------------------------
+
+Notice how we called `tpl_pack` in a loop, once for each element 0-9. Again,
+there is a corresponding unpacking <<var_unpack,example>> shown later in the
+guide. You might also notice that this time, we passed 1 as the final argument
+to tpl_pack. This is an index number designating which variable-length array
+we're packing. In this case, there is only one.
+
+[[index]]
+Index numbers
+^^^^^^^^^^^^^
+Index numbers identify a particular variable-length array in the format string.
+Each `A(...)` in a format string has its own index number. The index numbers
+are assigned left-to-right starting from 1. Examples:
+
+ A(i) /* index number 1 */
+ A(i)A(i) /* index numbers 1 and 2 */
+ A(A(i)) /* index numbers 1 and 2 (order is independent of nesting) */
+
+Special index number 0
+++++++++++++++++++++++
+The special index number 0 designates all the format characters that are not
+inside an `A(...)`. Examples of what index 0 does (and does not) designate:
+
+ S(ius) /* index 0 designates the whole thing */
+ iA(c)u /* index 0 designates the i and the u */
+ c#A(i)S(ci) /* index 0 designates the c# and the S(ci) */
+
+An index number is passed to `tpl_pack` and `tpl_unpack` to specify which
+variable-length array (or non-array, in the case of index number 0) to act upon.
+
+Integers
+~~~~~~~~
+The array examples <<fixed_pack,above>> demonstrated how integers could be
+packed. We'll show some further examples here of unpacking integers and dealing
+with multi-dimensional arrays. The same program could be used to demonstrate
+working with byte, 16-bit shorts, 32-bit or 64-bit signed and unsigned integers
+with only a change to the data type and the format character.
+
+[[fixed_unpack]]
+.Unpacking 0-9 from a fixed-length array
+--------------------------------------------------------------------------------
+#include "tpl.h"
+int main() {
+ tpl_node *tn;
+ int x[10];
+
+ tn = tpl_map("i#", x, 10);
+ tpl_load(tn, TPL_FILE, "/tmp/fixed.tpl");
+ tpl_unpack(tn,0); /* unpack all 10 elements at once */
+ tpl_free(tn);
+ /* now do something with x[0]...x[9].. (not shown */
+}
+--------------------------------------------------------------------------------
+
+For completeness, let's also see how to unpack a variable-length integer array.
+
+[[var_unpack]]
+.Unpacking 0-9 from a variable-length array
+-------------------------------------------------------------------------------
+#include "tpl.h"
+int main() {
+ tpl_node *tn;
+ int x;
+
+ tn = tpl_map("A(i)", &x);
+ tpl_load(tn, TPL_FILE, "/tmp/variable.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("%d\n",x); /* unpack one by one */
+ tpl_free(tn);
+}
+-------------------------------------------------------------------------------
+
+[[multidim_int]]
+Multi-dimensional arrays
+^^^^^^^^^^^^^^^^^^^^^^^^
+A multi-dimensional matrix of integers can be packed and unpacked the same way
+as any fixed-length array.
+
+ int xy[XDIM][YDIM];
+ ...
+ tn = tpl_map("i##", xy, XDIM, YDIM);
+ tpl_pack(tn, 0);
+
+This single call to `tpl_pack` packs the entire matrix.
+
+Strings
+~~~~~~~
+Tpl can serialize C strings. A different format is used for `char*` vs. `char[ ]`
+as described below. Let's look at `char*` first:
+
+.Packing a string
+-------------------------------------------------------------------------------
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ char *s = "hello, world!";
+ tn = tpl_map("s", &s);
+ tpl_pack(tn,0); /* copies "hello, world!" into the tpl */
+ tpl_dump(tn,TPL_FILE,"string.tpl");
+ tpl_free(tn);
+ }
+-------------------------------------------------------------------------------
+
+The `char*` must point to a null-terminated string or be a `NULL` pointer.
+
+When deserializing (unpacking) a C string, space for it will be allocated
+automatically, but you are responsible for freeing it (unless it is `NULL`):
+
+.Unpacking a string
+-------------------------------------------------------------------------------
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ char *s;
+ tn = tpl_map("s", &s);
+ tpl_load(tn,TPL_FILE,"string.tpl");
+ tpl_unpack(tn,0); /* allocates space, points s to "hello, world!" */
+ printf("unpacked %s\n", s);
+ free(s); /* our responsibility to free s */
+ tpl_free(tn);
+ }
+-------------------------------------------------------------------------------
+
+char* vs char[ ]
+^^^^^^^^^^^^^^^^
+The `s` format character is only for use with `char*` types. In the example
+above, `s` is a `char*`. If it had been a `char s[14]`, we would use the format
+characters `c#` to pack or unpack it, as a fixed-length character array. (This
+unpacks the characters "in-place", instead of into a dynamically allocated
+buffer). Also, a fixed-length buffer described by `c#` need not be
+null-terminated.
+
+Arrays of strings
+^^^^^^^^^^^^^^^^^
+You can use fixed- or variable-length arrays of strings in tpl. An example of
+packing a fixed-length two-dimensional array of strings is shown here.
+
+ char *labels[2][3] = { {"one", "two", "three"},
+ {"eins", "zwei", "drei" } };
+ tpl_node *tn;
+ tn = tpl_map("s##", labels, 2, 3);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+Later, when unpacking these strings, the programmer must remember to free them
+one by one, after they are no longer needed.
+
+ char *olabels[2][3];
+ int i,j;
+
+ tn = tpl_map("s##", olabels, 2, 3);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ for(i=0;i<2;i++) {
+ for(j=0;j<3;j++) {
+ printf("%s\n", olabels[i][j]);
+ free(olabels[i][j]);
+ }
+ }
+
+Binary buffers
+~~~~~~~~~~~~~~
+Packing an arbitrary-length binary buffer (tpl format character `B`) makes use
+of the `tpl_bin` structure. You must declare this structure and populate it
+with the address and length of the binary buffer to be packed.
+
+.Packing a binary buffer
+-------------------------------------------------------------------------------
+ #include "tpl.h"
+ #include <sys/time.h>
+
+ int main() {
+ tpl_node *tn;
+ tpl_bin tb;
+
+ /* we'll use a timeval as our guinea pig */
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+
+ tn = tpl_map( "B", &tb );
+ tb.sz = sizeof(struct timeval); /* size of buffer to pack */
+ tb.addr = &tv; /* address of buffer to pack */
+ tpl_pack( tn, 0 );
+ tpl_dump(tn, TPL_FILE, "bin.tpl");
+ tpl_free(tn);
+ }
+-------------------------------------------------------------------------------
+
+When you unpack a binary buffer, tpl will automatically allocate it, and will
+populate your `tpl_bin` structure with its address and length. You are
+responsible for eventually freeing the buffer.
+
+.Unpacking a binary buffer
+-------------------------------------------------------------------------------
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ tpl_bin tb;
+
+ tn = tpl_map( "B", &tb );
+ tpl_load( tn, TPL_FILE, "bin.tpl" );
+ tpl_unpack( tn, 0 );
+ tpl_free(tn);
+
+ printf("binary buffer of length %d at address %p\n", tb.sz, tb.addr);
+ free(tb.addr); /* our responsibility to free it */
+ }
+-------------------------------------------------------------------------------
+
+Structures
+~~~~~~~~~~
+You can use tpl to pack and unpack structures, and arrays of structures.
+
+ struct ci {
+ char c;
+ int i;
+ };
+ struct ci s = {'a', 1};
+
+ tn = tpl_map("S(ci)", &s); /* pass structure address */
+ tpl_pack(tn, 0);
+ tpl_dump(tn, TPL_FILE, "struct.tpl");
+ tpl_free(tn);
+
+As shown, omit the individual arguments for the format characters inside the
+parenthesis. The exception is for fixed-length arrays; when `S(...)` contains a
+`#` character, its length argument is required: `tpl_map("S(f#i)", &s, 10);`
+
+When using the `S(...)` format, the only characters allowed inside the
+parentheses are `iujvcsfIU#$()`.
+
+Structure arrays
+^^^^^^^^^^^^^^^^
+Arrays of structures are the same as simple arrays. Fixed- or variable- length
+arrays are supported.
+
+ struct ci sa[100], one;
+
+ tn = tpl_map("S(ci)#", sa, 100); /* fixed-length array of 100 structures */
+ tn = tpl_map("A(S(ci))", &one); /* variable-length array (one at a time)*/
+
+The differences between fixed- and variable-length arrays are explained in the
+<<arrays,Arrays>> section.
+
+Nested structures
+^^^^^^^^^^^^^^^^^
+When dealing with nested structures, the outermost structure uses the `S` format
+character, and the inner nested structures use the `$` format. Only the
+'outermost' structure's address is given to `tpl_map`.
+
+ struct inner_t {
+ char a;
+ }
+
+ struct outer_t {
+ char b;
+ struct inner_t i;
+ }
+
+ tpl_node *tn;
+ struct outer_t outer = {'b', {'a'}};
+
+ tn = tpl_map("S(c$(c))", &outer);
+
+Structures can nest to any level. Currently tpl does not support fixed-length
+array suffixes on inner structures. However the outermost structure can have a
+length suffix even if it contains some nested structures.
+
+Linked lists
+~~~~~~~~~~~~
+While tpl has no specific data type for a linked list, the technique for
+packing them is illustrated here. First describe your list element as a
+format string and then surround it with `A(...)` to describe it as
+variable-length array. Then, using a temporary variable, iterate over each
+list element, copying it to the temporary variable and packing it.
+
+ struct element {
+ char c;
+ int i;
+ struct element *next;
+ }
+
+ struct element *list, *i, tmp;
+ tpl_node *tn;
+
+ /* add some elements to list.. (not shown)*/
+
+ tn = tpl_map("A(ci)", &tmp);
+ for(i = list; i != NULL; i=i->next) {
+ tmp = *i;
+ tpl_pack(tn, 1);
+ }
+ tpl_dump(tn,TPL_FILE,"list.tpl");
+ tpl_free(tn);
+
+Unpacking is similar. The `for` loop is just replaced with:
+
+ while( tpl_unpack(tn,1) > 0) {
+ struct element *newelt = malloc(sizeof(struct element));
+ *newelt = tmp;
+ add_to_list(list, newelt);
+ }
+
+As you can see, tpl does not reinstate the whole list at once-- just one
+element at a time. You need to link the elements manually. A future release of
+tpl may support 'pointer swizzling' to make this easier.
+
+API
+---
+
+[[tpl_map]]
+tpl_map
+~~~~~~~
+The only way to create a tpl is to call `tpl_map()`. The first argument is the
+<<format,format string>>. This is followed by a list of arguments as required by
+the particular characters in the format string. E.g,
+
+ tpl_node *tn;
+ int i;
+ tn = tpl_map( "A(i)", &i );
+
+The function creates a mapping between the items in the format string and the C
+program variables whose addresses are given. Later, the C variables will be read
+or written as the tpl is packed or unpacked.
+
+This function returns a `tpl_node*` on success, or `NULL` on failure.
+
+[[tpl_pack]]
+tpl_pack
+~~~~~~~~
+The function `tpl_pack()` packs data into a tpl. The arguments to
+`tpl_pack()` are a `tpl_node*` and an <<index,index number>>.
+
+ tn = tpl_map("A(i)A(c)", &i, &c);
+ for(i=0; i<10; i++) tpl_pack(tn, 1); /* pack 0-9 into index 1 */
+ for(c='a; c<='z'; c++) tpl_pack(tn, 2); /* pack a-z into index 2 */
+
+.Data is copied when packed
+********************************************************************************
+Every call to `tpl_pack()` immediately 'copies' the data being packed. Thus
+the program is free to immediately overwrite or re-use the packed variables.
+********************************************************************************
+
+Index number 0
+^^^^^^^^^^^^^^
+It is necessary to pack index number 0 only if the format string contains
+characters that are not inside an `A(...)`, such as the `i` in the format string
+`iA(c)`.
+
+Variable-length arrays
+^^^^^^^^^^^^^^^^^^^^^^
+
+Adding elements to an array
++++++++++++++++++++++++++++
+To add elements to a variable-length array, call `tpl_pack()` repeatedly. Each
+call adds another element to the array.
+
+Zero-length arrays are ok
++++++++++++++++++++++++++
+It's perfectly acceptable to pack nothing into a variable-length array,
+resulting in a zero-length array.
+
+[[nested_pack]]
+Packing nested arrays
++++++++++++++++++++++
+In a format string containing a nested, variable-length array, such as
+`A(A(s))`, the inner, child array should be packed prior to the parent array.
+
+When you pack a parent array, a "snapshot" of the current child array is placed
+into the parent's new element. Packing a parent array also empties the child
+array. This way, you can pack new data into the child, then pack the parent
+again. This creates distinct parent elements which each contain distinct child
+arrays.
+
+[TIP]
+When dealing with nested arrays like `A(A(i))`, 'pack' them from the "inside
+out" (child first), but 'unpack' them from the "outside in" (parent first).
+
+The example below creates a tpl having the format string `A(A(c))`.
+
+.Packing nested arrays
+--------------------------------------------------------------------------------
+#include "tpl.h"
+
+int main() {
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(A(c))", &c);
+
+ for(c='a'; c<'c'; c++) tpl_pack(tn,2); /* pack child (twice) */
+ tpl_pack(tn, 1); /* pack parent */
+
+ for(c='1'; c<'4'; c++) tpl_pack(tn,2); /* pack child (three times) */
+ tpl_pack(tn, 1); /* pack parent */
+
+ tpl_dump(tn, TPL_FILE, "test40.tpl");
+ tpl_free(tn);
+}
+--------------------------------------------------------------------------------
+
+This creates a nested array in which the parent has two elements: the first
+element is the two-element nested array 'a', 'b'; and the second element is
+the three-element nested array '1', '2', '3'.
+The <<nested_unpack,nested unpacking example>> shows how this tpl is unpacked.
+
+[[tpl_dump]]
+tpl_dump
+~~~~~~~~
+After packing a tpl, `tpl_dump()` is used to write the tpl image to a file,
+memory buffer or file descriptor. The corresponding modes are shown below. A
+final mode is for querying the output size without actually performing the dump.
+
+[width="80%",options="header",cols="30^d,70m",grid="none"]
+|================================================================================
+|Write to... |Usage
+|file |tpl_dump(tn, TPL_FILE, "file.tpl" );
+|file descriptor |tpl_dump(tn, TPL_FD, 2);
+|memory |tpl_dump(tn, TPL_MEM, &addr, &len );
+|caller's memory |tpl_dump(tn, TPL_MEM\|TPL_PREALLOCD, buf, sizeof(buf));
+|just get size |tpl_dump(tn, TPL_GETSIZE, &sz);
+|================================================================================
+
+The first argument is the `tpl_node*` and the second is one of these constants:
+
+`TPL_FILE`::
+ Writes the tpl to a file whose name is given in the following argument.
+ The file is created with permissions 664 (`rw-rw-r--`) unless further
+ restricted by the process `umask`.
+`TPL_FD`::
+ Writes the tpl to the file descriptor given in the following argument.
+ The descriptor can be either blocking or non-blocking, but will busy-loop
+ if non-blocking and the contents cannot be written immediately.
+`TPL_MEM`::
+ Writes the tpl to a memory buffer. The following two arguments must be a
+ `void\*\*` and a `size_t*`. The function will allocate a buffer and store
+ its address and length into these locations. The caller is responsible to
+ `free()` the buffer when done using it.
+`TPL_MEM|TPL_PREALLOCD`::
+ Writes the tpl to a memory buffer that the caller has already allocated or
+ declared. The following two arguments must be a `void*` and a `size_t`
+ specifying the buffer address and size respectively. (If the buffer is of
+ insufficient size to receive the tpl dump, the function will return -1).
+ This mode can be useful in conjunction with `tpl_load` in `TPL_EXCESS_OK`
+ mode, as shown <<excess_ok,here.>>
+`TPL_GETSIZE`::
+ This special mode does not actually dump the tpl. Instead it places the size
+ that the dump 'would' require into the `uint32_t` pointed to by the
+ following argument.
+
+The return value is 0 on success, or -1 on error.
+
+The `tpl_dump()` function does not free the tpl. Use `tpl_free()` to release
+the tpl's resources when done.
+
+[TIP]
+.Back-to-back tpl images require no delimiter
+If you want to store a series of tpl images, or transmit sequential tpl images
+over a socket (perhaps as messages to another program), you can simply dump them
+sequentially without needing to add any delimiter for the individual tpl images.
+Tpl images are internally delimited, so `tpl_load` will read just one at a time
+even if multiple images are contiguous.
+
+[[tpl_load]]
+tpl_load
+~~~~~~~~
+This API function reads a previously-dumped tpl image from a file, memory
+buffer or file descriptor, and prepares it for subsequent unpacking. The format
+string specified in the preceding call to `tpl_map()` will be cross-checked
+for equality with the format string stored in the tpl image.
+
+ tn = tpl_map( "A(i)", &i );
+ tpl_load( tn, TPL_FILE, "demo.tpl" );
+
+The first argument to `tpl_load()` is the `tpl_node*`. The second argument is
+one of the constants:
+
+`TPL_FILE`::
+ Loads the tpl from the file named in the following argument. It is also
+ possible to bitwise-OR this flag with `TPL_EXCESS_OK` as explained below.
+`TPL_MEM`::
+ Loads the tpl from a memory buffer. The following two arguments must be a
+ `void*` and a `size_t`, specifying the buffer address and size,
+ respectively. The caller must not free the memory buffer until after
+ freeing the tpl with `tpl_free()`. (If the caller wishes to hand over
+ responsibility for freeing the memory buffer, so that it's automatically
+ freed along with the tpl when `tpl_free()` is called, the constant
+ `TPL_UFREE` may be bitwise-OR'd with `TPL_MEM` to achieve this).
+ Furthermore, `TPL_MEM` may be bitwise-OR'd with `TPL_EXCESS_OK`, explained
+ below.
+`TPL_FD`::
+ Loads the tpl from the file descriptor given in the following argument.
+ The descriptor is read until one complete tpl image is loaded; no bytes
+ past the end of the tpl image will be read. The descriptor can be either
+ blocking or non-blocking, but will busy-loop if non-blocking and the
+ contents cannot be read immediately.
+
+During loading, the tpl image will be extensively checked for internal validity.
+
+This function returns 0 on success or -1 on error.
+
+[[excess_ok]]
+`TPL_EXCESS_OK`
+^^^^^^^^^^^^^^^
+When reading a tpl image from a file or memory (but not from a file descriptor)
+the size of the file or memory buffer must exactly equal that of the tpl image
+stored therein. In other words, no excess trailing data beyond the tpl image is
+permitted. The bit flag `TPL_EXCESS_OK` can be OR'd with `TPL_MEM` or `TPL_FILE`
+to relax this requirement.
+
+A situation where this flag can be useful is in conjunction with `tpl_dump` in
+the `TPL_MEM|TPL_PREALLOCD` mode. In this example, the program does not concern
+itself with the actual tpl size as long as `LEN` is sufficiently large.
+
+ char buf[LEN]; /* will store and read tpl images here */
+ ...
+ tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buf, LEN);
+ ...
+ tpl_load(tn, TPL_MEM|TPL_EXCESS_OK, buf, LEN);
+
+
+
+
+[[tpl_unpack]]
+tpl_unpack
+~~~~~~~~~~
+The `tpl_unpack()` function unpacks data from the tpl. When data is unpacked,
+it is copied to the C program variables originally specified in `tpl_map()`.
+The first argument to `tpl_unpack` is the `tpl_node*` for the tpl and the
+second argument is an <<index,index number>>.
+
+ tn = tpl_map( "A(i)A(c)", &i, &c );
+ tpl_load( tn, TPL_FILE, "nested.tpl" );
+ while (tpl_unpack( tn, 1) > 0) printf("i is %d\n", i); /* unpack index 1 */
+ while (tpl_unpack( tn, 2) > 0) printf("c is %c\n", c); /* unpack index 2 */
+
+Index number 0
+^^^^^^^^^^^^^^
+It is necessary to unpack index number 0 only if the format string contains
+characters that are not inside an `A(...)`, such as the `i` in the format string
+`iA(c)`.
+
+Variable-length arrays
+^^^^^^^^^^^^^^^^^^^^^^
+
+Unpacking elements from an array
+++++++++++++++++++++++++++++++++
+For variable-length arrays, each call to `tpl_unpack()` unpacks another element.
+The return value can be used to tell when you're done: if it's positive, an
+element was unpacked; if it's 0, nothing was unpacked because there are no more
+elements. A negative retun value indicates an error (e.g. invalid index number).
+In this document, we usually unpack variable-length arrays using a `while` loop:
+
+ while( tpl_unpack( tn, 1 ) > 0 ) {
+ /* got another element */
+ }
+
+Array length
+++++++++++++
+When unpacking a variable-length array, it may be convenient to know ahead of
+time how many elements will need to be unpacked. You can use `tpl_Alen()` to
+get this number.
+
+[[nested_unpack]]
+Unpacking nested arrays
++++++++++++++++++++++++
+In a format string containing a nested variable-length array such as `A(A(s))`,
+unpack the outer, parent array before unpacking the child array.
+
+When you unpack a parent array, it prepares the child array for unpacking.
+After unpacking the elements of the child array, the program can repeat the
+process by unpacking another parent element, then the child elements, and so on.
+The example below unpacks a tpl having the format string `A(A(c))`.
+
+.Unpacking nested arrays
+--------------------------------------------------------------------------------
+#include "tpl.h"
+#include <stdio.h>
+
+int main() {
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(A(c))", &c);
+
+ tpl_load(tn, TPL_FILE, "test40.tpl");
+ while (tpl_unpack(tn,1) > 0) {
+ while (tpl_unpack(tn,2) > 0) printf("%c ",c);
+ printf("\n");
+ }
+ tpl_free(tn);
+}
+--------------------------------------------------------------------------------
+
+The file `test40.tpl` is from the <<nested_pack,nested packing example>>. When
+run, this program prints:
+
+ a b
+ 1 2 3
+
+[[tpl_free]]
+tpl_free
+~~~~~~~~
+The final step for any tpl is to release it using `tpl_free()`. Its only
+argument is the the `tpl_node*` to free.
+
+ tpl_free( tn );
+
+This function does not return a value (it is `void`).
+
+[[tpl_alen]]
+tpl_Alen
+~~~~~~~~
+This function takes a `tpl_node*` and an index number and returns an `int`
+specifying the number of elements in the variable-length array.
+
+ num_elements = tpl_Alen(tn, index);
+
+This is mainly useful for programs that unpack data and need to know ahead of
+time the number of elements that will need to be unpacked. (It returns the
+current number of elements; it will decrease as elements are unpacked).
+
+[[tpl_peek]]
+tpl_peek
+~~~~~~~~
+This function peeks into a file or a memory buffer containing a tpl image and
+and returns a copy of its format string. It can also peek at the lengths of
+any fixed-length arrays in the format string, or it can also peek into the data
+stored in the tpl.
+
+Format peek
+^^^^^^^^^^^
+The format string can be obtained
+like this:
+
+ fmt = tpl_peek(TPL_FILE, "file.tpl");
+ fmt = tpl_peek(TPL_MEM, addr, sz);
+
+On success, a copy of the format string is returned. The caller must eventually
+free it. On error, such as a non-existent file, or an invalid tpl image, it
+returns `NULL`.
+
+Array length peek
+^^^^^^^^^^^^^^^^^
+The lengths of all fixed-length arrays in the format string can be queried using
+the `TPL_FXLENS` mode. It provides the number of such fixed-length arrays and
+their lengths. If the former is non-zero, the caller must free the latter array
+when finished. The format string itself must also be freed.
+
+ uint32_t num_fxlens, *fxlens, j;
+ fmt = tpl_peek(TPL_FILE|TPL_FXLENS, filename, &num_fxlens, &fxlens);
+ if (fmt) {
+ printf("format %s, num_fxlens %u\n", fmt, num_fxlens);
+ for(j=0; j<num_fxlens; j++) printf("fxlens[%u] %u\n", j, fxlens[j]);
+ if (num_fxlens > 0) free(fxlens);
+ free(fmt);
+ }
+
+The `TPL_FXLENS` mode is mutually exclusive with `TPL_DATAPEEK`.
+
+
+Data peek
+^^^^^^^^^
+To peek into the data, additional arguments are used. This is a quick
+alternative to mapping, loading and unpacking the tpl, but peeking is limited
+to the data in index 0. In other words, no peeking into `A(...)` types.
+Suppose the tpl image in `file.tpl` has the format string `siA(i)`. Then the
+index 0 format characters are `si`. This is how to peek at their content:
+
+ char *s;
+ int i;
+ fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "si", &s, &i);
+
+Now `s`, `i`, and `fmt` have been populated with data. The caller must
+eventually free `fmt` and `s` because they are allocated strings.
+Of course, it works with `TPL_MEM` as well as `TPL_FILE`. Notice that
+`TPL_DATAPEEK` was OR'd with the mode. You can also specify 'any leading
+portion' of the index 0 format if you don't want to peek at the whole thing:
+
+ fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "s", &s);
+
+The `TPL_DATAPEEK` mode is mutually exclusive with `TPL_FXLENS`.
+
+Structure peek
+++++++++++++++
+Lastly you can peek into `S(...)` structures in index 0, but omit the
+surrounding `S(...)` in the format, and specify an argument to receive
+each structure member individually. You can specify any leading portion
+of the structure format. For example if `struct.tpl` has the format string
+`S(si)`, you can peek at its data in these ways:
+
+ fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "s", &s);
+ fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "si", &s, &i);
+
+[[tpl_jot]]
+tpl_jot
+~~~~~~~
+This is a quick shortcut for generating a tpl. It can be used instead of the
+usual "map, pack, dump, and free" lifecycle. With `tpl_jot` all those steps are
+handled for you. It only works for simple formats-- namely, those without
+`A(...)` in their format string. Here is how it is used:
+
+ char *hello = "hello", *world = "world";
+ tpl_jot( TPL_FILE, "file.tpl", "ss", &hello, &world);
+
+It supports the three standard modes, `TPL_FILE`, `TPL_FD` and `TPL_MEM`.
+It returns -1 on failure (such as a bad format string or error writing the
+file) or 0 on success.
+
+[[hooks]]
+tpl_hook
+~~~~~~~~
+Most users will just leave these hooks at their default values. You can change
+these hook values if you want to modify tpl's internal memory management and
+error reporting behavior.
+
+A global structure called `tpl_hook` encapsulates the hooks. A program can
+reconfigure any hook by specifying an alternative function whose prototype
+matches the default. For example:
+
+ #include "tpl.h"
+ extern tpl_hook_t tpl_hook;
+
+ int main() {
+ tpl_hook.oops = printf;
+ ...
+ }
+
+.Configurable hooks
+[width="90%",options="header",cols="m,d,m",grid="none"]
+|================================================================================
+|Hook |Description | Default
+|tpl_hook.oops |log error messages | tpl_oops
+|tpl_hook.malloc |allocate memory | malloc
+|tpl_hook.realloc |reallocate memory | realloc
+|tpl_hook.free |free memory | free
+|tpl_hook.fatal |log fatal message and exit | tpl_fatal
+|tpl_hook.gather_max |tpl_gather max image size | 0 (unlimited)
+|================================================================================
+
+The oops hook
+^^^^^^^^^^^^^
+The `oops` has the same prototype as `printf`. The built-in default oops
+handling function writes the error message to `stderr`.
+
+The fatal hook
+^^^^^^^^^^^^^^
+The fatal hook is invoked when a tpl function cannot continue because of an out-
+of-memory condition or some other usage violation or inconsistency. It has this
+prototype:
+
+ void fatal_fcn(char *fmt, ...);
+
+The `fatal` hook must not return. It must either exit, 'or' if the program needs
+to handle the failure and keep executing, `setjmp` and `longjmp` can be used.
+The default behavior is to `exit(-1)`.
+
+.Using longjmp in a fatal error handler
+--------------------------------------------------------------------------------
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "tpl.h"
+
+jmp_buf env;
+extern tpl_hook_t tpl_hook;
+
+void catch_fatal(char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ longjmp(env,-1); /* return to setjmp point */
+}
+
+int main() {
+ int err;
+ tpl_node *tn;
+ tpl_hook.fatal = catch_fatal; /* install fatal handler */
+
+ err = setjmp(env); /* on error, control will return here */
+ if (err) {
+ printf("caught error!\n");
+ return -1;
+ }
+
+ tn = tpl_map("@"); /* generate a fatal error */
+ printf("program ending, without error\n");
+ return 0;
+}
+--------------------------------------------------------------------------------
+
+This example is included in `tests/test123.c`. When run, this program prints:
+
+ unsupported option @
+ failed to parse @
+ caught error!
+
+
+tpl_gather
+~~~~~~~~~~
+
+.Most programs don't need this
+********************************************************************************
+Normally, `tpl_load()` is used to read a tpl image having an expected format
+string. A more generic operation is to acquire a tpl image whose format string is
+unknown. E.g., a generic message-receiving function might gather tpl images of
+varying format and route them to their final destination. This is the purpose of
+`tpl_gather`. It produces a memory buffer containing one tpl image. If there
+are multiple contiguous images in the input, it gathers exactly one image at a
+time.
+********************************************************************************
+
+The prototype for this function is:
+
+ int tpl_gather( int mode, ...);
+
+The `mode` argument is one of three constants listed below, which must be
+followed by the mode-specific required arguments:
+
+ TPL_GATHER_BLOCKING, int fd, void **img, size_t *sz
+ TPL_GATHER_NONBLOCKING, int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data
+ TPL_GATHER_MEM, void *addr, size_t sz, tpl_gather_t **gs, tpl_gather_cb *cb, void *data
+
+[NOTE]
+.`tpl_hook.gather_max`
+All modes honor `tpl_hook.gather_max`, specifying the maximum byte size for a
+tpl image to be gathered (the default is unlimited, signified by 0). If a source
+attempts to send a tpl image larger than this maximum, whatever partial image
+has been read will be discarded, and no further reading will take place; in this
+case `tpl_gather` will return a negative (error) value to inform the caller that
+it should stop gathering from this source, and close the originating file
+descriptor if there is one. (The whole idea is to prevent untrusted sources from
+sending extremely large tpl images which would consume too much memory.)
+
+
+`TPL_GATHER_BLOCKING`
+^^^^^^^^^^^^^^^^^^^
+In this mode, `tpl_gather` blocks while reading file descriptor `fd` until one
+complete tpl image is read. No bytes past the end of the tpl image will be read.
+The address of the buffer containing the image is returned in `img` and its size
+is placed in `sz`. The caller is responsible for eventually freeing the buffer.
+The function returns 1 on success, 0 on end-of-file, or a negative number on
+error.
+
+`TPL_GATHER_NONBLOCKING`
+^^^^^^^^^^^^^^^^^^^^^^
+This mode is for non-blocking, event-driven programs that implement their
+own file descriptor readability testing using `select()` or the like. In this
+mode, tpl images are gathered in chunks as data becomes readable. Whenever a
+full tpl image has been gathered, it invokes a caller-specified callback to do
+something with the image. The arguments are the file descriptor `fd` which the
+caller has determined to be readable and which must be in non-blocking mode, a
+pointer to a file-descriptor-specific handle which the caller has declared
+(explained below); a callback to invoke when a tpl image has been read; and an
+opaque pointer that will passed to the callback.
+
+For each file descriptor on which `tpl_gather` will be used, the caller must
+declare a `tpl_gather_t*` and initialize it to `NULL`. Thereafter it will be
+used internally by `tpl_gather` whenever data is readable on the descriptor.
+
+The callback will only be invoked whenever `tpl_gather()` has accumulated one
+complete tpl image. It must have this prototype:
+
+ int (tpl_gather_cb)(void *img, size_t sz, void *data);
+
+The callback can do anything with the tpl image but it must not free it. It can
+be copied if it needs to survive past the callback's return. The callback should
+return 0 under normal circumstances, or a negative number to abort; that is,
+returning a negative number causes `tpl_gather` itself to discard any remaining
+full or partial tpl images that have been read, and to return a negative number
+(-4 in particular) to signal its caller to close the file descriptor.
+
+The return value of `tpl_gather()` is negative if an error occured or 0 if a
+normal EOF was encountered-- both cases require that the caller close the file
+descriptor (and stop monitoring it for readability, obviously). If the return
+value is positive, the function succeeded in gathering whatever data was
+currently readable, which may have been a partial tpl image, or one or more
+complete images.
+
+Typical Usage
++++++++++++++
+The program will have established a file descriptor in non-blocking mode and
+be monitoring it for readability, using `select()`. Whenever it's readable, the
+program calls `tpl_gather()`. In skeletal terms:
+
+ tpl_gather_t *gt=NULL;
+ int rc;
+
+ void fd_is_readable(int fd) {
+ rc = tpl_gather( TPL_GATHER_NONBLOCKING, fd, >, callback, NULL );
+ if (rc <= 0) {
+ close(fd); /* got eof or fatal */
+ stop_watching_fd(fd);
+ }
+ }
+
+ int callback( void *img, size_t sz, void *data ) {
+ printf("got a tpl image\n"); /* do something with img. do not free it. */
+ return 0; /* normal (no error) */
+ }
+
+`TPL_GATHER_MEM`
+^^^^^^^^^^^^^^
+This mode is identical to `TPL_GATHER_NONBLOCKING` except that it gathers from a
+memory buffer instead of from a file descriptor. In other words, if some other
+layer of code-- say, a decryption function (that is decrypting fixed-size
+blocks) produces tpl fragments one-by-one, this mode can be used to reconstitute
+the tpl images and invoke the callback for each one. Its parameters are the same
+as for the `TPL_GATHER_NONBLOCKING` mode except that instead of a file
+descriptor, it takes a buffer address and size. The return values are also the
+same as for `TPL_GATHER_NONBLOCKING` noting of course there is no file
+descriptor to close on a non-positive return value.
+
+
+// vim: set tw=80 wm=2 syntax=asciidoc:
+
--- /dev/null
+package Tpl;
+
+# Copyright (c) 2005-2007, Troy Hanson http://tpl.sourceforge.net
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use strict;
+use warnings;
+use Config; # to get the size of "double" on this platform
+
+use bytes; # always use byte (not unicode char) offsets w/tpl images
+
+our $VERSION = 1.1;
+
+# tpl object is a reference to a hash with these keys:
+#
+# A(0):
+# ... :
+# A(n):
+#
+# where each A(i) refers to an A node, except A(0) is the root node.
+#
+# For each hash key (A node or root node), the value of that key is
+# a list reference. The members are of the list are the node's children.
+# They're represented as "Ai" (for A nodes) where i is a positive integer;
+# for non-A nodes the representation is [type,addr] e.g. [ "i", \$some_integer]
+#
+# For example,
+# Tpl->map("iA(ib)", \$x, \$y, \$z);
+# returns a tpl object which is a reference to a hash with these keys/values:
+#
+# $self->{A0} = [ [ "i", \$x ], "A1" ];
+# $self->{A1} = [ [ "i", \$y ], [ "b", \$z ] ];
+#
+# Now if A1 (that is, the "A(ib)" node) is packed, the tpl object acquires
+# another hash key/value:
+# $self->{P1} = [ $binary_int, $binary_byte ];
+# and repeated calls to pack A1 append further $binary elements.
+#
+sub tpl_map {
+ my $invocant = shift;
+ my $class = ref($invocant) || $invocant;
+ my $fmt = shift;
+ my @astack = (0); # stack of current A node's lineage in tpl tree
+ my $a_count=0; # running count of A's, thus an index of them
+ my $self = {}; # populate below
+ my ($lparen_level,$expect_lparen,$in_structure)=(0,0,0);
+ for (my $i=0; $i < length $fmt; $i++) {
+ my $c = substr($fmt,$i,1);
+ if ($c eq 'A') {
+ $a_count++;
+ push @{ $self->{"A" . $astack[-1]} }, "A$a_count";
+ push @astack, $a_count;
+ $expect_lparen=1;
+ } elsif ($c eq '(') {
+ die "invalid format $fmt" unless $expect_lparen;
+ $expect_lparen=0;
+ $lparen_level++;
+ } elsif ($c eq ')') {
+ $lparen_level--;
+ die "invalid format $fmt" if $lparen_level < 0;
+ die "invalid format $fmt" if substr($fmt,$i-1,1) eq '(';
+ if ($in_structure && ($in_structure-1 == $lparen_level)) {
+ $in_structure=0;
+ } else {
+ pop @astack; # rparen ends A() type, not S() type
+ }
+ } elsif ($c eq 'S') {
+ # in perl we just parse and ignore the S() construct
+ $expect_lparen=1;
+ $in_structure=1+$lparen_level; # so we can tell where S fmt ends
+ } elsif ($c =~ /^(i|u|B|s|c|f|I|U)$/) {
+ die "invalid format $fmt" if $expect_lparen;
+ my $r = shift;
+ die "no reference for $c (position $i of $fmt)" unless ref($r);
+ if (($c eq "f") and ($Config{doublesize} != 8)) {
+ die "double not 8 bytes on this platform";
+ }
+ if (($c =~ /(U|I)/) and not defined ($Config{use64bitint})) {
+ die "Tpl.pm: this 32-bit Perl can't pack/unpack 64-bit I/U integers\n";
+ }
+ push @{ $self->{"A" . $astack[-1]} }, [ $c , $r ];
+ } elsif ($c eq "#") {
+ # test for previous iucfIU
+ die "unallowed length modifer" unless $self->{"A" . $astack[-1]}->[-1]->[0] =~ /^(i|u|c|I|U|f)$/;
+ my $n = shift;
+ die "non-numeric # length modifer" unless $n =~ /^\d+$/;
+ push @{ $self->{"A" . $astack[-1]}->[-1] }, $n;
+ push @{ $self->{"#"}}, $n; # master array of octothorpe lengths
+ } else {
+ die "invalid character $c in format $fmt";
+ }
+ }
+ die "invalid format $fmt" if $lparen_level != 0;
+ $self->{fmt} = $fmt;
+ bless $self;
+ return $self;
+}
+
+sub tpl_format {
+ my $self = shift;
+ return $self->{fmt};
+}
+
+sub tpl_pack {
+ my $self = shift;
+ my $i = shift;
+ die "invalid index" unless defined $self->{"A$i"};
+ die "tpl for unpacking only" if defined $self->{"loaded"};
+ $self->{"packed"}++;
+ $self->{"P$i"} = undef if $i == 0; # node 0 doesn't accumulate
+ my @bb;
+ foreach my $node (@{ $self->{"A$i"} }) {
+ if (ref($node)) {
+ my ($type,$addr,$fxlen) = @{ $node };
+ if (defined $fxlen) { # octothorpic array
+ push @bb, CORE::pack("l$fxlen",@$addr) if $type eq "i"; # int
+ push @bb, CORE::pack("L$fxlen",@$addr) if $type eq "u"; # uint
+ push @bb, CORE::pack("C$fxlen",@$addr) if $type eq "c"; # byte
+ push @bb, CORE::pack("d$fxlen",@$addr) if $type eq "f"; # double
+ push @bb, CORE::pack("q$fxlen",@$addr) if $type eq "I"; # int64
+ push @bb, CORE::pack("Q$fxlen",@$addr) if $type eq "U"; # uint64
+ } else {
+ # non-octothorpic singleton
+ push @bb, CORE::pack("l",$$addr) if $type eq "i"; # int
+ push @bb, CORE::pack("L",$$addr) if $type eq "u"; # uint
+ push @bb, CORE::pack("C",$$addr) if $type eq "c"; # byte
+ push @bb, CORE::pack("d",$$addr) if $type eq "f"; # double (8 byte)
+ push @bb, CORE::pack("q",$$addr) if $type eq "I"; # int64
+ push @bb, CORE::pack("Q",$$addr) if $type eq "U"; # uint64
+ if ($type =~ /^(B|s)$/) { # string/binary
+ push @bb, CORE::pack("L", length($$addr));
+ push @bb, CORE::pack("a*", $$addr);
+ }
+ }
+ } elsif ($node =~ /^A(\d+)$/) {
+ # encode array length (int) and the array data into one scalar
+ my $alen = pack("l", scalar @{ $self->{"P$1"} or [] });
+ my $abod = (join "", @{ $self->{"P$1"} or [] });
+ push @bb, $alen . $abod;
+ $self->{"P$1"} = undef;
+ } else {
+ die "internal error; invalid node symbol $node";
+ }
+ }
+ push @{ $self->{"P$i"} }, (join "", @bb);
+}
+
+sub big_endian {
+ return (CORE::unpack("C", CORE::pack("L",1)) == 1) ? 0 : 1;
+}
+
+sub tpl_dump {
+ my $self = shift;
+ my $filename = shift;
+
+ $self->tpl_pack(0) if not defined $self->{"P0"};
+ my $format = $self->tpl_format;
+ my $octothorpe_lens = CORE::pack("L*", @{ $self->{"#"} or [] });
+ my $data = (join "", @{ $self->{"P0"} });
+ my $ov_len = length($format) + 1 + length($octothorpe_lens) + length($data) + 8;
+ my $flags = big_endian() ? 1 : 0;
+ my $preamble = CORE::pack("CLZ*", $flags, $ov_len, $format);
+ my $tpl = "tpl" . $preamble . $octothorpe_lens . $data;
+ return $tpl unless $filename;
+
+ # here for file output
+ open TPL, ">$filename" or die "can't open $filename: $!";
+ print TPL $tpl;
+ close TPL;
+}
+
+sub tpl_peek {
+ my $invocant = shift;
+ my $class = ref($invocant) || $invocant;
+ my $tplhandle = shift;
+ my $tpl;
+
+ if (ref($tplhandle)) {
+ $tpl = $$tplhandle;
+ } else {
+ open TPL, "<$tplhandle" or die "can't open $tplhandle: $!";
+ undef $/; # slurp
+ $tpl = <TPL>;
+ close TPL;
+ }
+ die "invalid tpl file" unless ($tpl =~ /^tpl/);
+ return (unpack("Z*", substr($tpl,8)));
+}
+
+sub tpl_load {
+ my $self = shift;
+ my $tplhandle = shift;
+
+ die "tpl for packing only" if $self->{"packed"};
+ die "tpl reloading not supported" if $self->{"loaded"};
+
+ # read tpl image from file or was it passed directly via ref?
+ my $tpl;
+ if (ref($tplhandle)) {
+ $tpl = $$tplhandle;
+ } else {
+ open TPL, "<$tplhandle" or die "can't open $tplhandle: $!";
+ undef $/; # slurp
+ $tpl = <TPL>;
+ close TPL;
+ }
+
+ $self->{"TI"} = $tpl;
+ $self->{"TL"} = length $tpl;
+ # verify preamble
+ die "invalid image -1" unless length($tpl) >= 9;
+ die "invalid image -2" unless $tpl =~ /^tpl/;
+ my $flags = CORE::unpack("C", substr($tpl,3,1));
+ $self->{"xendian"} = 1 if (big_endian() != ($flags & 1));
+ $self->{"UF"} = ($flags & 1) ? "N" : "V";
+ my $ov_len = CORE::unpack($self->{"UF"}, substr($tpl,4,4));
+ die "invalid image -3" unless $ov_len == length($tpl);
+ my $format = CORE::unpack("Z*", substr($tpl,8));
+ die "format mismatch" unless $format eq $self->tpl_format();
+ my @octothorpe_lens = @{ $self->{"#"} or [] };
+ my $ol = 8 + length($format) + 1; # start of octothorpe lengths
+ for (my $i=0; $i < (scalar @octothorpe_lens); $i++) {
+ my $len = CORE::unpack($self->{"UF"}, substr($tpl,$ol,4));
+ my $olen = $octothorpe_lens[$i];
+ die "fixed-length array size mismatch" unless $olen == $len;
+ $ol += 4;
+ }
+ my $dv = $ol; # start of packed data
+ my $len = $self->serlen("A0",$dv);
+ die "invalid image -4" if $len == -1;
+ die "invalid image -5" if (length($tpl) != $len + $dv);
+ $self->{"C0"} = $dv;
+ $self->{"loaded"} = 1;
+ $self->unpackA0; # prepare root child nodes for use
+}
+
+# byte reverse a word (any length)
+sub reversi {
+ my $word = shift;
+ my @w = split //, $word;
+ my $r = join "", (reverse @w);
+ return $r;
+}
+
+#
+# while unpacking, the object has these keys in its hash:
+# C0
+# C1
+# ...
+# C<n>
+# These are indices (into the tpl image $self->{"TI"}) from which node n
+# is being unpacked. I.e. as array elements of node n are unpacked, C<n>
+# advances through the tpl image.
+#
+# Similarly, elements
+# N1
+# N2
+# ...
+# N<n>
+# refer to the remaining array count for node n.
+#
+sub tpl_unpack {
+ my $self = shift;
+ my $n = shift;
+ my $ax = "A$n";
+ my $cx = "C$n";
+ my $nx = "N$n";
+ my $rc;
+
+ die "tpl for packing only" if $self->{"packed"};
+ die "tpl not loaded" unless $self->{"loaded"};
+
+ # decrement count for non root array nodes
+ if ($n > 0) {
+ return 0 if $self->{$nx} <= 0;
+ $rc = $self->{$nx}--;
+ }
+
+ for my $c (@{ $self->{$ax} }) {
+ if (ref($c)) {
+ my ($type,$addr,$fxlen) = @$c;
+ if (defined $fxlen) { # octothorpic unpack
+ @{ $addr } = (); # empty existing list before pushing elements
+ for(my $i=0; $i < $fxlen; $i++) {
+ if ($type eq "u") { # uint
+ push @{ $addr }, CORE::unpack($self->{"UF"},
+ substr($self->{"TI"},$self->{$cx},4));
+ $self->{$cx} += 4;
+ } elsif ($type eq "i") { #int (see note below re:signed int)
+ my $intbytes = substr($self->{"TI"},$self->{$cx},4);
+ $intbytes = reversi($intbytes) if $self->{"xendian"};
+ push @{ $addr }, CORE::unpack("l", $intbytes);
+ $self->{$cx} += 4;
+ } elsif ($type eq "c") { # byte
+ push @{ $addr }, CORE::unpack("C",
+ substr($self->{"TI"},$self->{$cx},1));
+ $self->{$cx} += 1;
+ } elsif ($type eq "f") { # double
+ my $double_bytes = substr($self->{"TI"},$self->{$cx},8);
+ $double_bytes = reversi($double_bytes) if $self->{"xendian"};
+ push @{ $addr }, CORE::unpack("d", $double_bytes );
+ $self->{$cx} += 8;
+ } elsif ($type eq "I") { #int64
+ my $intbytes = substr($self->{"TI"},$self->{$cx},8);
+ $intbytes = reversi($intbytes) if $self->{"xendian"};
+ push @{ $addr }, CORE::unpack("q", $intbytes);
+ $self->{$cx} += 8;
+ } elsif ($type eq "U") { #uint64
+ my $intbytes = substr($self->{"TI"},$self->{$cx},8);
+ $intbytes = reversi($intbytes) if $self->{"xendian"};
+ push @{ $addr }, CORE::unpack("Q", $intbytes);
+ $self->{$cx} += 8;
+ }
+ }
+ } else {
+ # non-octothorpe (singleton)
+ if ($type eq "u") { # uint
+ ${$addr} = CORE::unpack($self->{"UF"},
+ substr($self->{"TI"},$self->{$cx},4));
+ $self->{$cx} += 4;
+ } elsif ($type eq "i") { # int
+ # while perl's N or V conversions unpack an unsigned
+ # long from either big or little endian format
+ # respectively, when it comes to *signed* int, perl
+ # only has 'l' (which assumes native endianness).
+ # So we have to manually reverse the bytes in a
+ # cross-endian 'int' unpacking scenario.
+ my $intbytes = substr($self->{"TI"},$self->{$cx},4);
+ $intbytes = reversi($intbytes) if $self->{"xendian"};
+ ${$addr} = CORE::unpack("l", $intbytes);
+ $self->{$cx} += 4;
+ } elsif ($type eq 'c') { # byte
+ ${$c->[1]} = CORE::unpack("C",
+ substr($self->{"TI"},$self->{$cx},1));
+ $self->{$cx} += 1;
+ } elsif ($type eq 'f') { # double
+ ${$addr} = CORE::unpack("d",
+ substr($self->{"TI"},$self->{$cx},8));
+ $self->{$cx} += 8;
+ } elsif ($type =~ /^(B|s)$/) { # string/binary
+ my $slen = CORE::unpack($self->{"UF"},
+ substr($self->{"TI"},$self->{$cx},4));
+ $self->{$cx} += 4;
+ ${$addr} = CORE::unpack("a$slen",
+ substr($self->{"TI"},$self->{$cx},$slen));
+ $self->{$cx} += $slen;
+ } elsif ($type eq "I") { # int64
+ my $intbytes = substr($self->{"TI"},$self->{$cx},8);
+ $intbytes = reversi($intbytes) if $self->{"xendian"};
+ ${$addr} = CORE::unpack("q", $intbytes);
+ $self->{$cx} += 8;
+ } elsif ($type eq "U") { # uint64
+ my $intbytes = substr($self->{"TI"},$self->{$cx},8);
+ $intbytes = reversi($intbytes) if $self->{"xendian"};
+ ${$addr} = CORE::unpack("Q", $intbytes);
+ $self->{$cx} += 8;
+ } else { die "internal error"; }
+ }
+ } elsif ($c =~ /^A(\d+)$/) {
+ my $alen = $self->serlen($c,$self->{$cx});
+ $self->{"N$1"} = CORE::unpack($self->{"UF"},
+ substr($self->{"TI"},$self->{$cx},4)); # get array count
+ $self->{"C$1"} = $self->{$cx} + 4; # set array node's data start
+ $self->{$cx} += $alen; # step over array node's data
+ } else { die "internal error"; }
+ }
+
+ return $rc;
+}
+
+# specialized function to prepare root's child A nodes for initial use
+sub unpackA0 {
+ my $self = shift;
+ my $ax = "A0";
+ my $cx = "C0";
+ my $c0 = $self->{$cx};
+
+ for my $c (@{ $self->{$ax} }) {
+ next if ref($c); # skip non-A nodes
+ if ($c =~ /^A(\d+)$/) {
+ my $alen = $self->serlen($c,$c0);
+ $self->{"N$1"} = CORE::unpack($self->{"UF"},
+ substr($self->{"TI"},$c0,4)); # get array count
+ $self->{"C$1"} = $c0 + 4; # set array node's data start
+ $c0 += $alen; # step over array node's data
+ } else { die "internal error"; }
+ }
+}
+
+# ascertain serialized length of given node by walking
+sub serlen {
+ my $self = shift;
+ my $ax = shift;
+ my $dv = shift;
+
+ my $len = 0;
+
+ my $num;
+ if ($ax eq "A0") {
+ $num = 1;
+ } else {
+ return -1 unless $self->{"TL"} >= $dv + 4;
+ $num = CORE::unpack($self->{"UF"},substr($self->{"TI"},$dv,4));
+ $dv += 4;
+ $len += 4;
+ }
+
+ while ($num-- > 0) {
+ for my $c (@{ $self->{$ax} }) {
+ if (ref($c)) {
+ my $n = 1;
+ $n = $c->[2] if (@$c > 2); # octothorpic array length
+ if ($c->[0] =~ /^(i|u)$/) { # int/uint
+ return -1 unless $self->{"TL"} >= $dv + 4*$n;
+ $len += 4*$n;
+ $dv += 4*$n;
+ } elsif ($c->[0] eq "c") { # byte
+ return -1 unless $self->{"TL"} >= $dv + 1*$n;
+ $len += 1*$n;
+ $dv += 1*$n;
+ } elsif ($c->[0] eq "f") { # double
+ return -1 unless $self->{"TL"} >= $dv + 8*$n;
+ $len += 8*$n;
+ $dv += 8*$n;
+ } elsif ($c->[0] =~ /(I|U)/) { # int64/uint64
+ return -1 unless $self->{"TL"} >= $dv + 8*$n;
+ $len += 8*$n;
+ $dv += 8*$n;
+ } elsif ($c->[0] =~ /^(B|s)$/) { # string/binary
+ return -1 unless $self->{"TL"} >= $dv + 4;
+ my $slen = CORE::unpack($self->{"UF"},
+ substr($self->{"TI"},$dv,4));
+ $len += 4;
+ $dv += 4;
+ return -1 unless $self->{"TL"} >= $dv + $slen;
+ $len += $slen;
+ $dv += $slen;
+ } else { die "internal error" }
+ } elsif ($c =~ /^A/) {
+ my $alen = $self->serlen($c,$dv);
+ return -1 if $alen == -1;
+ $dv += $alen;
+ $len += $alen;
+ } else { die "internal error"; }
+ }
+ }
+ return $len;
+}
+
+1
--- /dev/null
+all: run_tests
+
+run_tests:
+ perl ./do_tests
+
+.PHONY: clean
+
+clean:
+ rm -f test*.out
--- /dev/null
+Run 'make' to run the tests.
+
+Run 'make clean' to clean up the temporary files.
+
+Description of tests
+=============================================================================
+test1 pack A(i) to file, unpack
+test2 pack A(i) to memory, unpack
+test3 pack A(i) to memory, pipe through tplxml (to make XML)
+test4 pack A(i) to file, convert to XML, convert back to tpl (using tplxml)
+test5 pack A(b) to file, unpack
+test6 pack A(b) to memory, pipe through tplxml (to make XML)
+test7 pack A(b) to file, convert to XML, convert back to tpl (using tplxml)
+test8 pack A(s) (has embedded &, < and > in strings to test quoting in XML)
+test9 pack A(u) to file, unpack
+test10 pack A(u) to memory, convert via tplxml
+test11 pack format B using a four-byte binary buffer, unpack and print
+test12 pack A(d) to file, unpack
+test13 pack A(d) to memory, convert via tplxml
+test14 unpack big-endian i (-2) (on little-endian machine, tests reversi)
+test15 unpack little-endian i (-3) (on big-endian machine, tests reversi)
+test16 pack to mem format B using a four-byte binary buffer, unpack and print
+test17 pack and unpack S(ic)
+test18 pack and unpack i#
+test19 pack and unpack i#i#
+test20 pack A(S(ci#)) to file, convert to XML, then back to tpl (cf test81.c)
+test21 Tpl->tpl_peek in-memory image
+test22 Tpl->tpl_peek file image
+test23 test I/U (only succeeds on 64-bit perl)
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use lib "..";
+use Tpl;
+
+our $port = 2000;
+
+# construct tpl
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+$tpl->tpl_pack(1) while ($i=shift @ARGV);
+my $request = $tpl->tpl_dump();
+
+# send to server, get response
+my $socket = IO::Socket::INET->new("localhost:$port") or die "can't connect";
+print $socket $request;
+shutdown($socket,1); # done writing (half-close)
+undef $/;
+my $response = <$socket>; # get reply (slurp)
+
+# decode response (or print error)
+my $total;
+my $tpl2 = Tpl->tpl_map("i", \$total);
+eval { $tpl2->tpl_load(\$response); };
+die "invalid response\n" if $@;
+$tpl2->tpl_unpack(0);
+print "total is $total\n";
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my @tests;
+for (glob "test*[0-9]") {
+ push @tests, $_ if -e "$_.ans";
+}
+
+my $num_failed=0;
+
+for my $test (@tests) {
+ `./$test > $test.out`;
+ `diff $test.out $test.ans`;
+ print "$test failed\n" if $?;
+ $num_failed++ if $?;
+}
+
+print scalar @tests . " tests conducted, $num_failed failed.\n";
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use lib "..";
+use Tpl;
+
+$SIG{CHLD} = "IGNORE"; # don't create zombies
+
+our $port = 2000;
+
+sub handle_client {
+ my $client = shift;
+
+ undef $/;
+ my $request = <$client>; # get request (slurp)
+
+ # read input array, and calculate total
+ my ($i,$total);
+ my $tpl = Tpl->tpl_map("A(i)", \$i);
+ eval { $tpl->tpl_load(\$request); };
+ die "received invalid tpl" if $@;
+ $total += $i while $tpl->tpl_unpack(1) > 0;
+
+ # formulate response and send
+ my $tpl2 = Tpl->tpl_map("i", \$total);
+ $tpl2->tpl_pack(0);
+ my $response = $tpl2->tpl_dump();
+ print $client $response;
+ close $client;
+}
+
+my $server = IO::Socket::INET->new(LocalPort => $port,
+ Type => SOCK_STREAM,
+ Reuse => 1,
+ Listen => 10 )
+ or die "Can't listen on port $port: $!\n";
+
+while (1) {
+ my $client = $server->accept();
+ next unless $client;
+ # new connection
+ my $pid = fork;
+ die "can't fork: $!\n" unless defined $pid;
+ if ($pid > 0) {
+ #p arent
+ close $client;
+ } elsif ($pid == 0) {
+ # child
+ handle_client($client);
+ exit(0);
+ }
+}
+close ($server);
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+$tpl->tpl_dump($tmp1);
+
+my $j;
+my $tpl2 = Tpl->tpl_map("A(i)",\$j);
+$tpl2->tpl_load($tmp1);
+while($tpl2->tpl_unpack(1) > 0) { print "$j\n" }
+
--- /dev/null
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(u)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+my $img = $tpl->tpl_dump();
+
+open TPLXML, "|../tplxml" or die "can't open tplxml: $!";
+print TPLXML $img;
+close TPLXML;
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+ <!DOCTYPE tplxml [
+ <!ELEMENT tplxml (A|i|u|I|U|B|s|c|f|fx)*>
+ <!ATTLIST tplxml
+ format CDATA #REQUIRED
+ fxlens CDATA #REQUIRED
+ >
+ <!ELEMENT i (#PCDATA)>
+ <!ELEMENT u (#PCDATA)>
+ <!ELEMENT I (#PCDATA)>
+ <!ELEMENT U (#PCDATA)>
+ <!ELEMENT B (#PCDATA)>
+ <!ELEMENT s (#PCDATA)>
+ <!ELEMENT c (#PCDATA)>
+ <!ELEMENT f (#PCDATA)>
+ <!ELEMENT A (el)*>
+ <!ELEMENT el (A|i|u|I|U|B|s|c|f|fx)+>
+ <!ELEMENT fx (i|u|I|U|c|f)*>
+ ]>
+<tplxml format="A(u)" fxlens="">
+ <A>
+ <el>
+ <u>0</u>
+ </el>
+ <el>
+ <u>1</u>
+ </el>
+ <el>
+ <u>2</u>
+ </el>
+ <el>
+ <u>3</u>
+ </el>
+ <el>
+ <u>4</u>
+ </el>
+ <el>
+ <u>5</u>
+ </el>
+ <el>
+ <u>6</u>
+ </el>
+ <el>
+ <u>7</u>
+ </el>
+ <el>
+ <u>8</u>
+ </el>
+ <el>
+ <u>9</u>
+ </el>
+ </A>
+</tplxml>
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("B",\$i);
+$i = pack("CCCC", 0xA, 0xB, 0xC, 0xD);
+$tpl->tpl_pack(0);
+$tpl->tpl_dump($tmp1);
+
+
+$tpl = Tpl->tpl_map("B",\$i);
+$tpl->tpl_load($tmp1);
+$tpl->tpl_unpack(0);
+print "$i\n";
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("A(f)",\$i);
+for($i=0; $i<10.0; $i+=2/3.0) { $tpl->tpl_pack(1); }
+$tpl->tpl_dump($tmp1);
+
+my $j;
+my $tpl2 = Tpl->tpl_map("A(f)",\$j);
+$tpl2->tpl_load($tmp1);
+while($tpl2->tpl_unpack(1) > 0) { printf("%.6f\n", $j); }
+
--- /dev/null
+0.000000
+0.666667
+1.333333
+2.000000
+2.666667
+3.333333
+4.000000
+4.666667
+5.333333
+6.000000
+6.666667
+7.333333
+8.000000
+8.666667
+9.333333
+10.000000
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(f)",\$i);
+for($i=0; $i<10.0; $i+=2/3.0) { $tpl->tpl_pack(1); }
+my $img = $tpl->tpl_dump();
+
+open TPLXML, "|../tplxml" or die "can't open tplxml: $!";
+print TPLXML $img;
+close TPLXML;
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+ <!DOCTYPE tplxml [
+ <!ELEMENT tplxml (A|i|u|I|U|B|s|c|f|fx)*>
+ <!ATTLIST tplxml
+ format CDATA #REQUIRED
+ fxlens CDATA #REQUIRED
+ >
+ <!ELEMENT i (#PCDATA)>
+ <!ELEMENT u (#PCDATA)>
+ <!ELEMENT I (#PCDATA)>
+ <!ELEMENT U (#PCDATA)>
+ <!ELEMENT B (#PCDATA)>
+ <!ELEMENT s (#PCDATA)>
+ <!ELEMENT c (#PCDATA)>
+ <!ELEMENT f (#PCDATA)>
+ <!ELEMENT A (el)*>
+ <!ELEMENT el (A|i|u|I|U|B|s|c|f|fx)+>
+ <!ELEMENT fx (i|u|I|U|c|f)*>
+ ]>
+<tplxml format="A(f)" fxlens="">
+ <A>
+ <el>
+ <f>0</f>
+ </el>
+ <el>
+ <f>0.666666666666667</f>
+ </el>
+ <el>
+ <f>1.33333333333333</f>
+ </el>
+ <el>
+ <f>2</f>
+ </el>
+ <el>
+ <f>2.66666666666667</f>
+ </el>
+ <el>
+ <f>3.33333333333333</f>
+ </el>
+ <el>
+ <f>4</f>
+ </el>
+ <el>
+ <f>4.66666666666667</f>
+ </el>
+ <el>
+ <f>5.33333333333333</f>
+ </el>
+ <el>
+ <f>6</f>
+ </el>
+ <el>
+ <f>6.66666666666667</f>
+ </el>
+ <el>
+ <f>7.33333333333333</f>
+ </el>
+ <el>
+ <f>8</f>
+ </el>
+ <el>
+ <f>8.66666666666667</f>
+ </el>
+ <el>
+ <f>9.33333333333333</f>
+ </el>
+ <el>
+ <f>10</f>
+ </el>
+ </A>
+</tplxml>
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $tmp1 = "test14.tpl";
+
+my $j;
+my $tpl2 = Tpl->tpl_map("i",\$j);
+$tpl2->tpl_load($tmp1);
+$tpl2->tpl_unpack(0);
+print "$j\n";
+
+
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $tmp1 = "test15.tpl";
+
+my $j;
+my $tpl2 = Tpl->tpl_map("i",\$j);
+$tpl2->tpl_load($tmp1);
+$tpl2->tpl_unpack(0);
+print "$j\n";
+
+
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $i;
+my $tpl = Tpl->tpl_map("B",\$i);
+$i = pack("CCCC", 0xA, 0xB, 0xC, 0xD);
+$tpl->tpl_pack(0);
+my $img = $tpl->tpl_dump();
+
+
+$tpl = Tpl->tpl_map("B",\$i);
+$tpl->tpl_load(\$img);
+$tpl->tpl_unpack(0);
+print "$i\n";
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my ($i,$j)=(1,ord('a'));
+my $tpl = Tpl->tpl_map("S(ic)",\$i, \$j);
+$tpl->tpl_pack(0);
+$tpl->tpl_dump($tmp1);
+
+($i,$j)=(-9,"x");
+my $tpl2 = Tpl->tpl_map("S(ic)",\$i,\$j);
+$tpl2->tpl_load($tmp1);
+$tpl2->tpl_unpack(0);
+$j = chr($j);
+print "$i,$j\n";
+
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my @i=(1,2,3,4);
+my $tpl = Tpl->tpl_map("i#",\@i, 3);
+$tpl->tpl_pack(0);
+$tpl->tpl_dump($tmp1);
+
+my @j;
+my $tpl2 = Tpl->tpl_map("i#",\@j,3);
+$tpl2->tpl_load($tmp1);
+$tpl2->tpl_unpack(0);
+print "$_\n" for @j;
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my @i=(1,2,3,4);
+my @j=(-1,-2,-3, -4);
+my $tpl = Tpl->tpl_map("i#i#",\@i, 3, \@j, 4);
+$tpl->tpl_pack(0);
+$tpl->tpl_dump($tmp1);
+
+my (@x,@y);
+my $tpl2 = Tpl->tpl_map("i#i#",\@x, 3, \@y, 4);
+$tpl2->tpl_load($tmp1);
+$tpl2->tpl_unpack(0);
+print "$_\n" for @x;
+print "$_\n" for @y;
--- /dev/null
+1
+2
+3
+-1
+-2
+-3
+-4
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+my $img = $tpl->tpl_dump();
+
+my $j;
+my $tpl2 = Tpl->tpl_map("A(i)",\$j);
+$tpl2->tpl_load(\$img);
+while($tpl2->tpl_unpack(1) > 0) { print "$j\n" }
+
--- /dev/null
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+my $tmp2 = "$pwd/$0_2.out";
+my $tmp3 = "$pwd/$0_3.out";
+
+my ($c,@i);
+my $tpl = Tpl->tpl_map("A(S(ci#))",\$c,\@i,10);
+
+# make element 1
+$c = 97;
+@i = (0,1,2,3,4,5,6,7,8,9);
+$tpl->tpl_pack(1);
+
+# make element 2
+$c = 98;
+@i = (1,2,3,4,5,6,7,8,9,10);
+$tpl->tpl_pack(1);
+
+$tpl->tpl_dump($tmp1);
+
+
+`../tplxml $tmp1 > $tmp2`; # convert tpl to xml
+`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl
+`diff $tmp1 $tmp3`;
+print "tpl files ", ($? ? "differ" : "identical"), "\n";
+
--- /dev/null
+tpl files identical
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+my $img = $tpl->tpl_dump();
+
+my $fmt = Tpl->tpl_peek(\$img);
+print("$fmt\n");
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my @i=(1,2,3,4);
+my @j=(-1,-2,-3, -4);
+my $tpl = Tpl->tpl_map("i#i#",\@i, 3, \@j, 4);
+$tpl->tpl_pack(0);
+$tpl->tpl_dump($tmp1);
+
+my $fmt = Tpl->tpl_peek($tmp1);
+print "$fmt\n";
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+# this test only works on 64-bit Perl
+
+my ($i,$j) = (-4294967296,4294967296); # 2^32 (can't fit in a 32-bit value)
+my $tpl = Tpl->tpl_map("IU",\$i,\$j);
+$tpl->tpl_pack(0);
+my $img = $tpl->tpl_dump();
+
+my ($x,$y);
+my $tpl2 = Tpl->tpl_map("IU",\$x,\$y);
+$tpl2->tpl_load(\$img);
+$tpl2->tpl_unpack(0);
+print "$x $y\n";
+
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+my $img = $tpl->tpl_dump();
+
+open TPLXML, "|../tplxml" or die "can't open tplxml: $!";
+print TPLXML $img;
+close TPLXML;
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+ <!DOCTYPE tplxml [
+ <!ELEMENT tplxml (A|i|u|I|U|B|s|c|f|fx)*>
+ <!ATTLIST tplxml
+ format CDATA #REQUIRED
+ fxlens CDATA #REQUIRED
+ >
+ <!ELEMENT i (#PCDATA)>
+ <!ELEMENT u (#PCDATA)>
+ <!ELEMENT I (#PCDATA)>
+ <!ELEMENT U (#PCDATA)>
+ <!ELEMENT B (#PCDATA)>
+ <!ELEMENT s (#PCDATA)>
+ <!ELEMENT c (#PCDATA)>
+ <!ELEMENT f (#PCDATA)>
+ <!ELEMENT A (el)*>
+ <!ELEMENT el (A|i|u|I|U|B|s|c|f|fx)+>
+ <!ELEMENT fx (i|u|I|U|c|f)*>
+ ]>
+<tplxml format="A(i)" fxlens="">
+ <A>
+ <el>
+ <i>0</i>
+ </el>
+ <el>
+ <i>1</i>
+ </el>
+ <el>
+ <i>2</i>
+ </el>
+ <el>
+ <i>3</i>
+ </el>
+ <el>
+ <i>4</i>
+ </el>
+ <el>
+ <i>5</i>
+ </el>
+ <el>
+ <i>6</i>
+ </el>
+ <el>
+ <i>7</i>
+ </el>
+ <el>
+ <i>8</i>
+ </el>
+ <el>
+ <i>9</i>
+ </el>
+ </A>
+</tplxml>
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+my $tmp2 = "$pwd/$0_2.out";
+my $tmp3 = "$pwd/$0_3.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+$tpl->tpl_dump($tmp1);
+
+
+`../tplxml $tmp1 > $tmp2`; # convert tpl to xml
+`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl
+`diff $tmp1 $tmp3`;
+print "tpl files ", ($? ? "differ" : "identical"), "\n";
+
--- /dev/null
+tpl files identical
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("A(c)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+$tpl->tpl_dump($tmp1);
+
+my $j;
+my $tpl2 = Tpl->tpl_map("A(c)",\$j);
+$tpl2->tpl_load($tmp1);
+while($tpl2->tpl_unpack(1) > 0) { printf("%d\n", $j); }
+
--- /dev/null
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(c)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+my $img = $tpl->tpl_dump();
+
+open TPLXML, "|../tplxml" or die "can't open tplxml: $!";
+print TPLXML $img;
+close TPLXML;
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+ <!DOCTYPE tplxml [
+ <!ELEMENT tplxml (A|i|u|I|U|B|s|c|f|fx)*>
+ <!ATTLIST tplxml
+ format CDATA #REQUIRED
+ fxlens CDATA #REQUIRED
+ >
+ <!ELEMENT i (#PCDATA)>
+ <!ELEMENT u (#PCDATA)>
+ <!ELEMENT I (#PCDATA)>
+ <!ELEMENT U (#PCDATA)>
+ <!ELEMENT B (#PCDATA)>
+ <!ELEMENT s (#PCDATA)>
+ <!ELEMENT c (#PCDATA)>
+ <!ELEMENT f (#PCDATA)>
+ <!ELEMENT A (el)*>
+ <!ELEMENT el (A|i|u|I|U|B|s|c|f|fx)+>
+ <!ELEMENT fx (i|u|I|U|c|f)*>
+ ]>
+<tplxml format="A(c)" fxlens="">
+ <A>
+ <el>
+ <c>0</c>
+ </el>
+ <el>
+ <c>1</c>
+ </el>
+ <el>
+ <c>2</c>
+ </el>
+ <el>
+ <c>3</c>
+ </el>
+ <el>
+ <c>4</c>
+ </el>
+ <el>
+ <c>5</c>
+ </el>
+ <el>
+ <c>6</c>
+ </el>
+ <el>
+ <c>7</c>
+ </el>
+ <el>
+ <c>8</c>
+ </el>
+ <el>
+ <c>9</c>
+ </el>
+ </A>
+</tplxml>
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+my $tmp2 = "$pwd/$0_2.out";
+my $tmp3 = "$pwd/$0_3.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("A(c)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+$tpl->tpl_dump($tmp1);
+
+
+`../tplxml $tmp1 > $tmp2`; # convert tpl to xml
+`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl
+`diff $tmp1 $tmp3`;
+print "tpl files ", ($? ? "differ" : "identical"), "\n";
+
--- /dev/null
+tpl files identical
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+my $tmp2 = "$pwd/$0_2.out";
+my $tmp3 = "$pwd/$0_3.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("A(s)",\$i);
+for (qw(normal has&ersand <anglebrackets>)) {
+ $i = $_;
+ $tpl->tpl_pack(1);
+}
+$tpl->tpl_dump($tmp1);
+
+
+`../tplxml $tmp1 > $tmp2`; # convert tpl to xml
+`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl
+`diff $tmp1 $tmp3`;
+print "tpl files ", ($? ? "differ" : "identical"), "\n";
+
--- /dev/null
+tpl files identical
--- /dev/null
+#!/usr/bin/perl
+#
+use strict;
+use warnings;
+
+use lib "..";
+use Tpl;
+
+my $pwd = `pwd`;
+chomp $pwd;
+
+my $tmp1 = "$pwd/$0_1.out";
+
+my $i;
+my $tpl = Tpl->tpl_map("A(u)",\$i);
+for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); }
+$tpl->tpl_dump($tmp1);
+
+my $j;
+my $tpl2 = Tpl->tpl_map("A(u)",\$j);
+$tpl2->tpl_load($tmp1);
+while($tpl2->tpl_unpack(1) > 0) { print "$j\n" }
+
--- /dev/null
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
--- /dev/null
+#!/usr/bin/perl
+
+# tplfmt
+# by Troy Hanson Feb 2006
+# print the format string of a tpl image file
+
+use strict;
+use warnings;
+
+sub peek_fmt {
+ my $buf = shift;
+ die "invalid tpl file" unless ($$buf =~ /^tpl/);
+ return (unpack("Z*", substr($$buf,8)));
+}
+
+die "usage: $0 <file> [<file> ...]" unless (@ARGV > 0);
+
+undef $/; # slurp
+for (@ARGV) {
+ open TPL, "<$_" or die "can't open $_: $!";
+ my $tpl = <TPL>;
+ print "$_: ", peek_fmt(\$tpl), "\n";
+ close TPL;
+}
--- /dev/null
+#!/usr/bin/perl
+
+# tplxml
+# by Troy Hanson 27 Feb 2006
+# convert between tpl and XML
+
+# Copyright (c) 2005-2006, Troy Hanson http://tpl.sourceforge.net
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use strict;
+use warnings;
+use XML::Parser;
+use FindBin;
+use lib "$FindBin::Bin"; #locate Tpl.pm in same directory as tplxml
+use Tpl;
+use bytes;
+
+sub quote_chars {
+ my $str = shift;
+ $$str =~ s/&/&/g; #order matters
+ $$str =~ s/</</g;
+ $$str =~ s/>/>/g;
+}
+sub unquote_chars {
+ my $str = shift;
+ $$str =~ s/</</g;
+ $$str =~ s/>/>/g;
+ $$str =~ s/&/&/g;
+}
+sub hex_chars {
+ my $str = shift;
+ my $hex;
+ for(my $i=0; $i < length $$str; $i++) {
+ my $byte = unpack("C",substr($$str,$i,1));
+ $hex .= sprintf("%02x", $byte);
+ }
+ $$str = $hex;
+}
+sub unhex_chars {
+ my $str = shift;
+ my $bytes;
+ for(my $i=0; $i < length $$str; $i+=2) {
+ my $hexbyte = substr($$str,$i,2);
+ $bytes .= pack("C", hex($hexbyte));
+ }
+ $$str= $bytes;
+}
+
+sub tpl2xml {
+ my $src = shift;
+ my (@out,@args);
+
+ # build list of references to hold output of unpacking
+ my ($fmt,@fxlens) = peek_fmt($src);
+ for(my ($i,$j,$k)=(0,0,0);$i<length($fmt);$i++) {
+ push @args, [] if substr($fmt,$i,2) =~ /^[iucfIU]\#$/; # octothorpic
+ push @args, \$out[$j++] if substr($fmt,$i,2) =~ /^[iuBscfIU][^\#]*$/;
+ push @args, $fxlens[$k++] if substr($fmt,$i,1) eq "#";
+ }
+
+ my $tpl = Tpl->tpl_map($fmt,@args);
+ $tpl->tpl_load($src);
+ $tpl->tpl_unpack(0);
+
+ # construct xml preamble
+ my $pre = qq{<?xml version="1.0" encoding="utf-8" ?>
+ <!DOCTYPE tplxml [
+ <!ELEMENT tplxml (A|i|u|I|U|B|s|c|f|fx)*>
+ <!ATTLIST tplxml
+ format CDATA #REQUIRED
+ fxlens CDATA #REQUIRED
+ >
+ <!ELEMENT i (#PCDATA)>
+ <!ELEMENT u (#PCDATA)>
+ <!ELEMENT I (#PCDATA)>
+ <!ELEMENT U (#PCDATA)>
+ <!ELEMENT B (#PCDATA)>
+ <!ELEMENT s (#PCDATA)>
+ <!ELEMENT c (#PCDATA)>
+ <!ELEMENT f (#PCDATA)>
+ <!ELEMENT A (el)*>
+ <!ELEMENT el (A|i|u|I|U|B|s|c|f|fx)+>
+ <!ELEMENT fx (i|u|I|U|c|f)*>
+ ]>\n};
+ print $pre;
+ my $fxattr = join ",", @fxlens;
+ print qq{<tplxml format="$fmt" fxlens="$fxattr">\n};
+ tpl2xml_node($tpl,"A0",1);
+ print qq{</tplxml>\n};
+}
+
+sub tpl2xml_node {
+ my $tpl = shift;
+ my $node = shift;
+ my $indent = shift;
+ my $i = " " x $indent;
+ for my $c (@{ $tpl->{$node} }) {
+ if (ref($c)) {
+ my ($type,$addr,$fxlen) = @$c;
+ quote_chars $addr if $type eq 's';
+ hex_chars $addr if $type eq 'B';
+ if (not defined $fxlen) {
+ print qq{$i<$type>$$addr</$type>\n}; # singleton
+ } else {
+ # all elements of octothorpic fixed-len array
+ print qq{$i<fx>\n};
+ print qq{$i <$type>$addr->[$_]</$type>\n} for (0..$fxlen-1);
+ print qq{$i</fx>\n};
+ }
+ } else {
+ # A node
+ print qq{$i<A>\n};
+ my $idx = $1 if $c =~ /^A(\d+)$/;
+ while($tpl->tpl_unpack($idx) > 0) {
+ print qq{$i<el>\n};
+ tpl2xml_node($tpl,$c,$indent+1);
+ print qq{$i</el>\n};
+ }
+ print qq{$i</A>\n};
+ }
+ }
+}
+
+sub xml2tpl {
+ my $src = shift;
+ my $p = new XML::Parser( Style => 'Tree' );
+ my $tree = $p->parse($$src);
+ die "not a tpl xml document" unless $tree->[0] eq 'tplxml';
+ die "no format attribute" unless defined $tree->[1][0]->{format};
+ my $fmt = $tree->[1][0]->{format};
+ die "no fxlens attribute" unless defined $tree->[1][0]->{fxlens};
+ my @fxlens = split /,/, $tree->[1][0]->{fxlens};
+
+ # build list of references to variables for use in packing
+ my (@args,@out);
+ for(my ($i,$j,$k)=(0,0,0);$i<length($fmt);$i++) {
+ push @args, [] if substr($fmt,$i,2) =~ /^[iucfIU]\#$/; # octothorpic
+ push @args, \$out[$j++] if substr($fmt,$i,2) =~ /^[iuBscfIU][^\#]*$/;
+ push @args, $fxlens[$k++] if substr($fmt,$i,1) eq "#";
+ }
+
+ my $tpl = Tpl->tpl_map($fmt,@args);
+ xml2tpl_dfs($tpl,$tree->[1]);
+ $tpl->tpl_pack(0);
+ print $tpl->tpl_dump;
+}
+
+sub xml2tpl_dfs {
+ my $tpl = shift;
+ my $xml = shift;
+
+ my @next = @$xml; # ($attr,@tagvals) = $$xml;
+ shift @next; # discard <tplxml> attributes
+ my @tpltoks = @{ $tpl->{"A0"} }; #expected tokens when parsing
+
+ TAG: while (@next) {
+ my $xmltag = shift @next;
+ my $xmlval = shift @next;
+
+ # skip whitespace/newlines embedded between tags
+ next TAG if ($xmltag eq "0" and $xmlval =~ /^\s+$/);
+
+ # pack if necessary. consume tokens by look-ahead until non-pack token.
+ while (@tpltoks > 0 and $tpltoks[0] =~ /^P(\d+)$/) {
+ shift @tpltoks;
+ $tpl->tpl_pack($1);
+ }
+
+ # If tpl format specifies a non-array type should appear at this point
+ # in the XML tree, then validate the type matches the format and assign
+ # the value from the XML to the variable from which it'll be packed
+ my $tpltoken = shift @tpltoks;
+ my $octothorpic=0;
+ if (ref $tpltoken) {
+ my ($tpltype,$tpladdr,$fxlen) = @$tpltoken;
+
+ # This block is how we handle octothorpic (fixed length) arrays.
+ # If $fxlen is defined then an octothorpic <fx> node is expected.
+ # After finding the <fx> node we put its subnodes (the array elements)
+ # onto the @next array for immediate parsing and we use $fxlen:$remaining
+ # as a signet version of the $fxlen to induce the element-processing loop.
+ if (defined $fxlen) {
+ if ($fxlen =~ /^(\d+):(\d+)$/) { # $1==orig $fxlen, $2==remain $fxlen
+ $octothorpic=1;
+ unshift @tpltoks, [$tpltype, $tpladdr, $1.":".($2-1)] if $2 > 1;
+ } else { # octothorpic array expected; look for <fx> parent node
+ die "expected '<fx>' but got '<$xmltag>'" unless $xmltag eq 'fx';
+ @{ $tpladdr } = (); # Empty accumulator array for octothorpic values
+ unshift @tpltoks, [$tpltype, $tpladdr, "$fxlen:$fxlen"]; # x:x signet
+ shift @$xmlval; # discard 'A' attributes
+ unshift @next, @$xmlval; #parse xml subtree now (dfs)
+ next TAG; # proceed to children of <fx> node
+ }
+ }
+
+ if ($tpltype ne $xmltag) {
+ die "mismatch: xml has '$xmltag' where format specifies '$tpltype'";
+ }
+ # expect @$xmlval to be ({},0,'value') i.e. a single, terminal text node
+ if (@$xmlval > 3 || $xmlval->[1] ne '0') {
+ die "error: xml tag '$xmltag' cannot enclose sub-tags";
+ }
+ if ($octothorpic) {
+ push @{ $tpladdr }, $xmlval->[2];
+ } else {
+ $$tpladdr = $xmlval->[2];
+ }
+ unquote_chars $tpladdr if $tpltype eq 's';
+ unhex_chars $tpladdr if $tpltype eq 'B';
+ } elsif ($tpltoken =~ /^A(\d+)$/) {
+ # tpl format specifies an array should appear at this point in the XML
+ if ($xmltag ne 'A') {
+ die "mismatch: xml has '$xmltag' where format specifies 'A'";
+ }
+ shift @$xmlval; # discard 'A' attributes
+
+ # form token that means "replace me with tokens from A(n), x times"
+ # (where x is the number of elements contained by this array).
+ my $array_count=0;
+ for(my $i=0; $i < @$xmlval; $i+=2) {
+ $array_count++ if $xmlval->[$i] eq 'el';
+ }
+
+ unshift @tpltoks, "N$1:$array_count" if $array_count > 0;
+ unshift @next, @$xmlval; #parse xml subtree now (dfs)
+ } elsif ($tpltoken =~ /^N(\d+):(\d+)$/) {
+ if ($xmltag ne "el") {
+ die "mismatch: xml has '$xmltag' where array 'el' is expected";
+ }
+ # prepend A$1's tokens (and decremented N:count) to expected tokens
+ my ($n,$elsleft) = ($1, ($2 - 1));
+ unshift @tpltoks, "N$n:$elsleft" if $elsleft > 0;
+ unshift @tpltoks, "P$n"; # "pack me now" token
+ unshift @tpltoks, @{ $tpl->{"A$1"} };
+
+ shift @$xmlval; # discard 'el' attributes
+ unshift @next, @$xmlval; # proceed to parse el subtree (dfs)
+ } else {
+ die "internal error, unexpected token $tpltoken";
+ }
+ }
+
+ # pack if necessary. consume tokens by look-ahead until non-pack token.
+ while (@tpltoks > 0 and $tpltoks[0] =~ /^P(\d+)$/) {
+ shift @tpltoks;
+ $tpl->tpl_pack($1);
+ }
+
+ if (@tpltoks > 0) {
+ die "error: end of xml document reached but format requires more data";
+ }
+}
+
+sub peek_fmt {
+ my $buf = shift;
+ die "invalid tpl file" unless ($$buf =~ /^tpl/);
+ my $flags = CORE::unpack("C", substr($$buf,3,1));
+ my $UF = ($flags & 1) ? "N" : "V"; # big or little endian fxlens
+ my $fmt = (CORE::unpack("Z*", substr($$buf,8)));
+ my $num_octothorpes = scalar (my @o = ($fmt =~ /#/g));
+ my @fxlens;
+ my $fx = 8 + length($fmt) + 1;
+ for(my $i=0; $i < $num_octothorpes; $i++) {
+ my $fxlen_bytes = substr($$buf,$fx,4);
+ my $fxlen = unpack($UF, $fxlen_bytes);
+ push @fxlens, $fxlen;
+ $fx += 4;
+ }
+ return ($fmt,@fxlens);
+}
+
+##########################################################################
+# Slurp input file, auto-detect if conversion is to tpl or XML, and run.
+##########################################################################
+
+undef $/;
+my $src = <>;
+our $to = (substr($src,0,3) eq "tpl") ? "xml" : "tpl";
+xml2tpl(\$src) if $to eq "tpl";
+tpl2xml(\$src) if $to eq "xml";
+
--- /dev/null
+SUBDIRS = win
+lib_LTLIBRARIES = libtpl.la
+libtpl_la_SOURCES = tpl.c
+include_HEADERS = tpl.h
+libtpl_la_LDFLAGS = -no-undefined -version-info 0:0:0
+libtpl_la_LIBADD = win/libwinmmap.la
+
--- /dev/null
+# Makefile.in generated by automake 1.10.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 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@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@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@
+subdir = src
+DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config/config.h
+CONFIG_CLEAN_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 = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libtpl_la_DEPENDENCIES = win/libwinmmap.la
+am_libtpl_la_OBJECTS = tpl.lo
+libtpl_la_OBJECTS = $(am_libtpl_la_OBJECTS)
+libtpl_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libtpl_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/config
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libtpl_la_SOURCES)
+DIST_SOURCES = $(libtpl_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+includeHEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(include_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+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@
+MAKEINFO = @MAKEINFO@
+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_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_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+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@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = win
+lib_LTLIBRARIES = libtpl.la
+libtpl_la_SOURCES = tpl.c
+include_HEADERS = tpl.h
+libtpl_la_LDFLAGS = -no-undefined -version-info 0:0:0
+libtpl_la_LIBADD = win/libwinmmap.la
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(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'; \
+ 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
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libtpl.la: $(libtpl_la_OBJECTS) $(libtpl_la_DEPENDENCIES)
+ $(libtpl_la_LINK) -rpath $(libdir) $(libtpl_la_OBJECTS) $(libtpl_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tpl.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
+ $(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
+ rm -f "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+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 $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-includeHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+ install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags ctags-recursive \
+ 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-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-recursive uninstall uninstall-am \
+ uninstall-includeHEADERS 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:
--- /dev/null
+/*
+Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define TPL_VERSION 1.5
+
+static const char id[]="$Id: tpl.c 192 2009-04-24 10:35:30Z thanson $";
+
+
+#include <stdlib.h> /* malloc */
+#include <stdarg.h> /* va_list */
+#include <string.h> /* memcpy, memset, strchr */
+#include <stdio.h> /* printf (tpl_hook.oops default function) */
+
+#ifndef _WIN32
+#include <unistd.h> /* for ftruncate */
+#else
+#include <io.h>
+#define ftruncate(x,y) _chsize(x,y)
+#endif
+#include <sys/types.h> /* for 'open' */
+#include <sys/stat.h> /* for 'open' */
+#include <fcntl.h> /* for 'open' */
+#include <errno.h>
+#ifndef _WIN32
+#include <inttypes.h> /* uint32_t, uint64_t, etc */
+#else
+typedef unsigned short ushort;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#endif
+
+
+#if ( defined __CYGWIN__ || defined __MINGW32__ || defined _WIN32 )
+#include "win/mman.h" /* mmap */
+#else
+#include <sys/mman.h> /* mmap */
+#endif
+
+#include "tpl.h"
+
+#define TPL_GATHER_BUFLEN 8192
+#define TPL_MAGIC "tpl"
+
+/* macro to add a structure to a doubly-linked list */
+#define DL_ADD(head,add) \
+ do { \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (head)->prev->next = (add); \
+ (head)->prev = (add); \
+ (add)->next = NULL; \
+ } else { \
+ (head)=(add); \
+ (head)->prev = (head); \
+ (head)->next = NULL; \
+ } \
+ } while (0);
+
+#define fatal_oom() tpl_hook.fatal("out of memory\n")
+
+/* bit flags (internal). preceded by the external flags in tpl.h */
+#define TPL_WRONLY (1 << 9) /* app has initiated tpl packing */
+#define TPL_RDONLY (1 << 10) /* tpl was loaded (for unpacking) */
+#define TPL_XENDIAN (1 << 11) /* swap endianness when unpacking */
+#define TPL_OLD_STRING_FMT (1 << 12) /* tpl has strings in 1.2 format */
+
+/* values for the flags byte that appears after the magic prefix */
+#define TPL_SUPPORTED_BITFLAGS 3
+#define TPL_FL_BIGENDIAN (1 << 0)
+#define TPL_FL_NULLSTRINGS (1 << 1)
+
+/* char values for node type */
+#define TPL_TYPE_ROOT 0
+#define TPL_TYPE_INT32 1
+#define TPL_TYPE_UINT32 2
+#define TPL_TYPE_BYTE 3
+#define TPL_TYPE_STR 4
+#define TPL_TYPE_ARY 5
+#define TPL_TYPE_BIN 6
+#define TPL_TYPE_DOUBLE 7
+#define TPL_TYPE_INT64 8
+#define TPL_TYPE_UINT64 9
+#define TPL_TYPE_INT16 10
+#define TPL_TYPE_UINT16 11
+#define TPL_TYPE_POUND 12
+
+/* error codes */
+#define ERR_NOT_MINSIZE (-1)
+#define ERR_MAGIC_MISMATCH (-2)
+#define ERR_INCONSISTENT_SZ (-3)
+#define ERR_FMT_INVALID (-4)
+#define ERR_FMT_MISSING_NUL (-5)
+#define ERR_FMT_MISMATCH (-6)
+#define ERR_FLEN_MISMATCH (-7)
+#define ERR_INCONSISTENT_SZ2 (-8)
+#define ERR_INCONSISTENT_SZ3 (-9)
+#define ERR_INCONSISTENT_SZ4 (-10)
+#define ERR_UNSUPPORTED_FLAGS (-11)
+
+/* access to A(...) nodes by index */
+typedef struct tpl_pidx {
+ struct tpl_node *node;
+ struct tpl_pidx *next,*prev;
+} tpl_pidx;
+
+/* A(...) node datum */
+typedef struct tpl_atyp {
+ uint32_t num; /* num elements */
+ size_t sz; /* size of each backbone's datum */
+ struct tpl_backbone *bb,*bbtail;
+ void *cur;
+} tpl_atyp;
+
+/* backbone to extend A(...) lists dynamically */
+typedef struct tpl_backbone {
+ struct tpl_backbone *next;
+ /* when this structure is malloc'd, extra space is alloc'd at the
+ * end to store the backbone "datum", and data points to it. */
+#if __STDC_VERSION__ < 199901
+ char *data;
+#else
+ char data[];
+#endif
+} tpl_backbone;
+
+/* mmap record */
+typedef struct tpl_mmap_rec {
+ int fd;
+ void *text;
+ size_t text_sz;
+} tpl_mmap_rec;
+
+/* root node datum */
+typedef struct tpl_root_data {
+ int flags;
+ tpl_pidx *pidx;
+ tpl_mmap_rec mmap;
+ char *fmt;
+ int *fxlens, num_fxlens;
+} tpl_root_data;
+
+/* node type to size mapping */
+struct tpl_type_t {
+ char c;
+ int sz;
+};
+
+
+/* Internal prototypes */
+static tpl_node *tpl_node_new(tpl_node *parent);
+static tpl_node *tpl_find_i(tpl_node *n, int i);
+static void *tpl_cpv(void *datav, void *data, size_t sz);
+static void *tpl_extend_backbone(tpl_node *n);
+static char *tpl_fmt(tpl_node *r);
+static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv);
+static size_t tpl_ser_osz(tpl_node *n);
+static void tpl_free_atyp(tpl_node *n,tpl_atyp *atyp);
+static int tpl_dump_to_mem(tpl_node *r, void *addr, size_t sz);
+static int tpl_mmap_file(char *filename, tpl_mmap_rec *map_rec);
+static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out);
+static int tpl_cpu_bigendian(void);
+static int tpl_needs_endian_swap(void *);
+static void tpl_byteswap(void *word, int len);
+static void tpl_fatal(char *fmt, ...);
+static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen);
+static int tpl_unpackA0(tpl_node *r);
+static int tpl_oops(const char *fmt, ...);
+static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data);
+static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data);
+static int tpl_gather_blocking(int fd, void **img, size_t *sz);
+static tpl_node *tpl_map_va(char *fmt, va_list ap);
+
+/* This is used internally to help calculate padding when a 'double'
+ * follows a smaller datatype in a structure. Normally under gcc
+ * on x86, d will be aligned at +4, however use of -malign-double
+ * causes d to be aligned at +8 (this is actually faster on x86).
+ * Also SPARC and x86_64 seem to align always on +8.
+ */
+struct tpl_double_alignment_detector {
+ char a;
+ double d; /* some platforms align this on +4, others on +8 */
+};
+
+/* this is another case where alignment varies. mac os x/gcc was observed
+ * to align the int64_t at +4 under -m32 and at +8 under -m64 */
+struct tpl_int64_alignment_detector {
+ int i;
+ int64_t j; /* some platforms align this on +4, others on +8 */
+};
+
+typedef struct {
+ size_t inter_elt_len; /* padded inter-element len; i.e. &a[1].field - &a[0].field */
+ tpl_node *iter_start_node; /* node to jump back to, as we start each new iteration */
+ size_t iternum; /* current iteration number (total req'd. iter's in n->num) */
+} tpl_pound_data;
+
+/* Hooks for customizing tpl mem alloc, error handling, etc. Set defaults. */
+tpl_hook_t tpl_hook = {
+ /* .oops = */ tpl_oops,
+ /* .malloc = */ malloc,
+ /* .realloc = */ realloc,
+ /* .free = */ free,
+ /* .fatal = */ tpl_fatal,
+ /* .gather_max = */ 0 /* max tpl size (bytes) for tpl_gather */
+};
+
+static const char tpl_fmt_chars[] = "AS($)BiucsfIUjv#"; /* valid format chars */
+static const char tpl_S_fmt_chars[] = "iucsfIUjv#$()"; /* valid within S(...) */
+static const char tpl_datapeek_ok_chars[] = "iucsfIUjv"; /* valid in datapeek */
+static const struct tpl_type_t tpl_types[] = {
+ /* [TPL_TYPE_ROOT] = */ {'r', 0},
+ /* [TPL_TYPE_INT32] = */ {'i', sizeof(int32_t)},
+ /* [TPL_TYPE_UINT32] = */ {'u', sizeof(uint32_t)},
+ /* [TPL_TYPE_BYTE] = */ {'c', sizeof(char)},
+ /* [TPL_TYPE_STR] = */ {'s', sizeof(char*)},
+ /* [TPL_TYPE_ARY] = */ {'A', 0},
+ /* [TPL_TYPE_BIN] = */ {'B', 0},
+ /* [TPL_TYPE_DOUBLE] = */ {'f', 8}, /* not sizeof(double) as that varies */
+ /* [TPL_TYPE_INT64] = */ {'I', sizeof(int64_t)},
+ /* [TPL_TYPE_UINT64] = */ {'U', sizeof(uint64_t)},
+ /* [TPL_TYPE_INT16] = */ {'j', sizeof(int16_t)},
+ /* [TPL_TYPE_UINT16] = */ {'v', sizeof(uint16_t)},
+ /* [TPL_TYPE_POUND] = */ {'#', 0},
+};
+
+/* default error-reporting function. Just writes to stderr. */
+static int tpl_oops(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap,fmt);
+ vfprintf(stderr,fmt,ap);
+ va_end(ap);
+ return 0;
+}
+
+
+static tpl_node *tpl_node_new(tpl_node *parent) {
+ tpl_node *n;
+ if ((n=tpl_hook.malloc(sizeof(tpl_node))) == NULL) {
+ fatal_oom();
+ }
+ n->addr=NULL;
+ n->data=NULL;
+ n->num=1;
+ n->ser_osz=0;
+ n->children=NULL;
+ n->next=NULL;
+ n->parent=parent;
+ return n;
+}
+
+/* Used in S(..) formats to pack several fields from a structure based on
+ * only the structure address. We need to calculate field addresses
+ * manually taking into account the size of the fields and intervening padding.
+ * The wrinkle is that double is not normally aligned on x86-32 but the
+ * -malign-double compiler option causes it to be. Double are aligned
+ * on Sparc, and apparently on 64 bit x86. We use a helper structure
+ * to detect whether double is aligned in this compilation environment.
+ */
+char *calc_field_addr(tpl_node *parent, int type,char *struct_addr, int ordinal) {
+ tpl_node *prev;
+ int offset;
+ int align_sz;
+
+ if (ordinal == 1) return struct_addr; /* first field starts on structure address */
+
+ /* generate enough padding so field addr is divisible by it's align_sz. 4, 8, etc */
+ prev = parent->children->prev;
+ switch(type) {
+ case TPL_TYPE_DOUBLE:
+ align_sz = sizeof(struct tpl_double_alignment_detector) > 12 ? 8 : 4;
+ break;
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ align_sz = sizeof(struct tpl_int64_alignment_detector) > 12 ? 8 : 4;
+ break;
+ default:
+ align_sz = tpl_types[type].sz;
+ break;
+ }
+ offset = ((uintptr_t)prev->addr - (uintptr_t)struct_addr)
+ + (tpl_types[prev->type].sz * prev->num);
+ offset = (offset + align_sz - 1) / align_sz * align_sz;
+ return struct_addr + offset;
+}
+
+TPL_API tpl_node *tpl_map(char *fmt,...) {
+ va_list ap;
+ tpl_node *tn;
+
+ va_start(ap,fmt);
+ tn = tpl_map_va(fmt, ap);
+ va_end(ap);
+ return tn;
+}
+
+static tpl_node *tpl_map_va(char *fmt, va_list ap) {
+ int lparen_level=0,expect_lparen=0,t=0,in_structure=0,ordinal=0;
+ int in_nested_structure=0;
+ char *c, *peek, *struct_addr=NULL, *struct_next;
+ tpl_node *root,*parent,*n=NULL,*preceding,*iter_start_node=NULL,
+ *struct_widest_node=NULL, *np; tpl_pidx *pidx;
+ tpl_pound_data *pd;
+ int *fxlens, num_fxlens, pound_num, pound_prod, applies_to_struct;
+ int contig_fxlens[10]; /* temp space for contiguous fxlens */
+ int num_contig_fxlens, i, j;
+ ptrdiff_t inter_elt_len=0; /* padded element length of contiguous structs in array */
+
+
+ root = tpl_node_new(NULL);
+ root->type = TPL_TYPE_ROOT;
+ root->data = (tpl_root_data*)tpl_hook.malloc(sizeof(tpl_root_data));
+ if (!root->data) fatal_oom();
+ memset((tpl_root_data*)root->data,0,sizeof(tpl_root_data));
+
+ /* set up root nodes special ser_osz to reflect overhead of preamble */
+ root->ser_osz = sizeof(uint32_t); /* tpl leading length */
+ root->ser_osz += strlen(fmt) + 1; /* fmt + NUL-terminator */
+ root->ser_osz += 4; /* 'tpl' magic prefix + flags byte */
+
+ parent=root;
+
+ c=fmt;
+ while (*c != '\0') {
+ switch (*c) {
+ case 'c':
+ case 'i':
+ case 'u':
+ case 'j':
+ case 'v':
+ case 'I':
+ case 'U':
+ case 'f':
+ if (*c=='c') t=TPL_TYPE_BYTE;
+ else if (*c=='i') t=TPL_TYPE_INT32;
+ else if (*c=='u') t=TPL_TYPE_UINT32;
+ else if (*c=='j') t=TPL_TYPE_INT16;
+ else if (*c=='v') t=TPL_TYPE_UINT16;
+ else if (*c=='I') t=TPL_TYPE_INT64;
+ else if (*c=='U') t=TPL_TYPE_UINT64;
+ else if (*c=='f') t=TPL_TYPE_DOUBLE;
+
+ if (expect_lparen) goto fail;
+ n = tpl_node_new(parent);
+ n->type = t;
+ if (in_structure) {
+ if (ordinal == 1) {
+ /* for S(...)# iteration. Apply any changes to case 's' too!!! */
+ iter_start_node = n;
+ struct_widest_node = n;
+ }
+ if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
+ struct_widest_node = n;
+ }
+ n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
+ } else n->addr = (void*)va_arg(ap,void*);
+ n->data = tpl_hook.malloc(tpl_types[t].sz);
+ if (!n->data) fatal_oom();
+ if (n->parent->type == TPL_TYPE_ARY)
+ ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz;
+ DL_ADD(parent->children,n);
+ break;
+ case 's':
+ if (expect_lparen) goto fail;
+ n = tpl_node_new(parent);
+ n->type = TPL_TYPE_STR;
+ if (in_structure) {
+ if (ordinal == 1) {
+ iter_start_node = n; /* for S(...)# iteration */
+ struct_widest_node = n;
+ }
+ if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
+ struct_widest_node = n;
+ }
+ n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
+ } else n->addr = (void*)va_arg(ap,void*);
+ n->data = tpl_hook.malloc(sizeof(char*));
+ if (!n->data) fatal_oom();
+ *(char**)(n->data) = NULL;
+ if (n->parent->type == TPL_TYPE_ARY)
+ ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
+ DL_ADD(parent->children,n);
+ break;
+ case '#':
+ /* apply a 'num' to preceding atom */
+ if (!parent->children) goto fail;
+ preceding = parent->children->prev; /* first child's prev is 'last child'*/
+ t = preceding->type;
+ applies_to_struct = (*(c-1) == ')') ? 1 : 0;
+ if (!applies_to_struct) {
+ if (!(t == TPL_TYPE_BYTE || t == TPL_TYPE_INT32 ||
+ t == TPL_TYPE_UINT32 || t == TPL_TYPE_DOUBLE ||
+ t == TPL_TYPE_UINT64 || t == TPL_TYPE_INT64 ||
+ t == TPL_TYPE_UINT16 || t == TPL_TYPE_INT16 ||
+ t == TPL_TYPE_STR )) goto fail;
+ }
+ /* count up how many contiguous # and form their product */
+ pound_prod=1;
+ num_contig_fxlens=0;
+ for(peek=c; *peek == '#'; peek++) {
+ pound_num = va_arg(ap, int);
+ if (pound_num < 1) {
+ tpl_hook.fatal("non-positive iteration count %d\n", pound_num);
+ }
+ if (num_contig_fxlens >= (sizeof(contig_fxlens)/sizeof(contig_fxlens[0]))) {
+ tpl_hook.fatal("contiguous # exceeds hardcoded limit\n");
+ }
+ contig_fxlens[num_contig_fxlens++] = pound_num;
+ pound_prod *= pound_num;
+ }
+ /* increment c to skip contiguous # so its points to last one */
+ c = peek-1;
+ /* differentiate atom-# from struct-# by noting preceding rparen */
+ if (applies_to_struct) { /* insert # node to induce looping */
+ n = tpl_node_new(parent);
+ n->type = TPL_TYPE_POUND;
+ n->num = pound_prod;
+ n->data = tpl_hook.malloc(sizeof(tpl_pound_data));
+ if (!n->data) fatal_oom();
+ pd = (tpl_pound_data*)n->data;
+ pd->inter_elt_len = inter_elt_len;
+ pd->iter_start_node = iter_start_node;
+ pd->iternum = 0;
+ DL_ADD(parent->children,n);
+ /* multiply the 'num' and data space on each atom in the structure */
+ for(np = iter_start_node; np != n; np = np->next) {
+ if (n->parent->type == TPL_TYPE_ARY) {
+ ((tpl_atyp*)(n->parent->data))->sz +=
+ tpl_types[np->type].sz * (np->num * (n->num - 1));
+ }
+ np->data = tpl_hook.realloc(np->data, tpl_types[np->type].sz *
+ np->num * n->num);
+ if (!np->data) fatal_oom();
+ memset(np->data, 0, tpl_types[np->type].sz * np->num * n->num);
+ }
+ } else { /* simple atom-# form does not require a loop */
+ preceding->num = pound_prod;
+ preceding->data = tpl_hook.realloc(preceding->data,
+ tpl_types[t].sz * preceding->num);
+ if (!preceding->data) fatal_oom();
+ memset(preceding->data,0,tpl_types[t].sz * preceding->num);
+ if (n->parent->type == TPL_TYPE_ARY) {
+ ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz *
+ (preceding->num-1);
+ }
+ }
+ root->ser_osz += (sizeof(uint32_t) * num_contig_fxlens);
+
+ j = ((tpl_root_data*)root->data)->num_fxlens; /* before incrementing */
+ (((tpl_root_data*)root->data)->num_fxlens) += num_contig_fxlens;
+ num_fxlens = ((tpl_root_data*)root->data)->num_fxlens; /* new value */
+ fxlens = ((tpl_root_data*)root->data)->fxlens;
+ fxlens = tpl_hook.realloc(fxlens, sizeof(int) * num_fxlens);
+ if (!fxlens) fatal_oom();
+ ((tpl_root_data*)root->data)->fxlens = fxlens;
+ for(i=0; i < num_contig_fxlens; i++) fxlens[j++] = contig_fxlens[i];
+
+ break;
+ case 'B':
+ if (expect_lparen) goto fail;
+ if (in_structure) goto fail;
+ n = tpl_node_new(parent);
+ n->type = TPL_TYPE_BIN;
+ n->addr = (tpl_bin*)va_arg(ap,void*);
+ n->data = tpl_hook.malloc(sizeof(tpl_bin*));
+ if (!n->data) fatal_oom();
+ *((tpl_bin**)n->data) = NULL;
+ if (n->parent->type == TPL_TYPE_ARY)
+ ((tpl_atyp*)(n->parent->data))->sz += sizeof(tpl_bin);
+ DL_ADD(parent->children,n);
+ break;
+ case 'A':
+ if (in_structure) goto fail;
+ n = tpl_node_new(parent);
+ n->type = TPL_TYPE_ARY;
+ DL_ADD(parent->children,n);
+ parent = n;
+ expect_lparen=1;
+ pidx = (tpl_pidx*)tpl_hook.malloc(sizeof(tpl_pidx));
+ if (!pidx) fatal_oom();
+ pidx->node = n;
+ pidx->next = NULL;
+ DL_ADD(((tpl_root_data*)(root->data))->pidx,pidx);
+ /* set up the A's tpl_atyp */
+ n->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
+ if (!n->data) fatal_oom();
+ ((tpl_atyp*)(n->data))->num = 0;
+ ((tpl_atyp*)(n->data))->sz = 0;
+ ((tpl_atyp*)(n->data))->bb = NULL;
+ ((tpl_atyp*)(n->data))->bbtail = NULL;
+ ((tpl_atyp*)(n->data))->cur = NULL;
+ if (n->parent->type == TPL_TYPE_ARY)
+ ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
+ break;
+ case 'S':
+ if (in_structure) goto fail;
+ expect_lparen=1;
+ ordinal=1; /* index upcoming atoms in S(..) */
+ in_structure=1+lparen_level; /* so we can tell where S fmt ends */
+ struct_addr = (char*)va_arg(ap,void*);
+ break;
+ case '$': /* nested structure */
+ if (!in_structure) goto fail;
+ expect_lparen=1;
+ in_nested_structure++;
+ break;
+ case ')':
+ lparen_level--;
+ if (lparen_level < 0) goto fail;
+ if (*(c-1) == '(') goto fail;
+ if (in_nested_structure) in_nested_structure--;
+ else if (in_structure && (in_structure-1 == lparen_level)) {
+ /* calculate delta between contiguous structures in array */
+ struct_next = calc_field_addr(parent, struct_widest_node->type,
+ struct_addr, ordinal++);
+ inter_elt_len = struct_next - struct_addr;
+ in_structure=0;
+ }
+ else parent = parent->parent; /* rparen ends A() type, not S() type */
+ break;
+ case '(':
+ if (!expect_lparen) goto fail;
+ expect_lparen=0;
+ lparen_level++;
+ break;
+ default:
+ tpl_hook.oops("unsupported option %c\n", *c);
+ goto fail;
+ }
+ c++;
+ }
+ if (lparen_level != 0) goto fail;
+
+ /* copy the format string, save for convenience */
+ ((tpl_root_data*)(root->data))->fmt = tpl_hook.malloc(strlen(fmt)+1);
+ if (((tpl_root_data*)(root->data))->fmt == NULL)
+ fatal_oom();
+ memcpy(((tpl_root_data*)(root->data))->fmt,fmt,strlen(fmt)+1);
+
+ return root;
+
+fail:
+ tpl_hook.oops("failed to parse %s\n", fmt);
+ tpl_free(root);
+ return NULL;
+}
+
+static int tpl_unmap_file( tpl_mmap_rec *mr) {
+
+ if ( munmap( mr->text, mr->text_sz ) == -1 ) {
+ tpl_hook.oops("Failed to munmap: %s\n", strerror(errno));
+ }
+ close(mr->fd);
+ mr->text = NULL;
+ mr->text_sz = 0;
+ return 0;
+}
+
+static void tpl_free_keep_map(tpl_node *r) {
+ int mmap_bits = (TPL_RDONLY|TPL_FILE);
+ int ufree_bits = (TPL_MEM|TPL_UFREE);
+ tpl_node *nxtc,*c;
+ int find_next_node=0,looking,i;
+ size_t sz;
+
+ /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
+ if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
+ tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
+ } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
+ tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
+ }
+
+ c = r->children;
+ if (c) {
+ while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
+ switch (c->type) {
+ case TPL_TYPE_BIN:
+ /* free any binary buffer hanging from tpl_bin */
+ if ( *((tpl_bin**)(c->data)) ) {
+ if ( (*((tpl_bin**)(c->data)))->addr ) {
+ tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
+ }
+ *((tpl_bin**)c->data) = NULL; /* reset tpl_bin */
+ }
+ find_next_node=1;
+ break;
+ case TPL_TYPE_STR:
+ /* free any packed (copied) string */
+ for(i=0; i < c->num; i++) {
+ char *str = ((char**)c->data)[i];
+ if (str) {
+ tpl_hook.free(str);
+ ((char**)c->data)[i] = NULL;
+ }
+ }
+ find_next_node=1;
+ break;
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ case TPL_TYPE_POUND:
+ find_next_node=1;
+ break;
+ case TPL_TYPE_ARY:
+ c->ser_osz = 0; /* zero out the serialization output size */
+
+ sz = ((tpl_atyp*)(c->data))->sz; /* save sz to use below */
+ tpl_free_atyp(c,c->data);
+
+ /* make new atyp */
+ c->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
+ if (!c->data) fatal_oom();
+ ((tpl_atyp*)(c->data))->num = 0;
+ ((tpl_atyp*)(c->data))->sz = sz; /* restore bb datum sz */
+ ((tpl_atyp*)(c->data))->bb = NULL;
+ ((tpl_atyp*)(c->data))->bbtail = NULL;
+ ((tpl_atyp*)(c->data))->cur = NULL;
+
+ c = c->children;
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+
+ if (find_next_node) {
+ find_next_node=0;
+ looking=1;
+ while(looking) {
+ if (c->next) {
+ nxtc=c->next;
+ c=nxtc;
+ looking=0;
+ } else {
+ if (c->type == TPL_TYPE_ROOT) break; /* root node */
+ else {
+ nxtc=c->parent;
+ c=nxtc;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ((tpl_root_data*)(r->data))->flags = 0; /* reset flags */
+}
+
+TPL_API void tpl_free(tpl_node *r) {
+ int mmap_bits = (TPL_RDONLY|TPL_FILE);
+ int ufree_bits = (TPL_MEM|TPL_UFREE);
+ tpl_node *nxtc,*c;
+ int find_next_node=0,looking,i;
+ tpl_pidx *pidx,*pidx_nxt;
+
+ /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
+ if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
+ tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
+ } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
+ tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
+ }
+
+ c = r->children;
+ if (c) {
+ while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
+ switch (c->type) {
+ case TPL_TYPE_BIN:
+ /* free any binary buffer hanging from tpl_bin */
+ if ( *((tpl_bin**)(c->data)) ) {
+ if ( (*((tpl_bin**)(c->data)))->sz != 0 ) {
+ tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
+ }
+ tpl_hook.free(*((tpl_bin**)c->data)); /* free tpl_bin */
+ }
+ tpl_hook.free(c->data); /* free tpl_bin* */
+ find_next_node=1;
+ break;
+ case TPL_TYPE_STR:
+ /* free any packed (copied) string */
+ for(i=0; i < c->num; i++) {
+ char *str = ((char**)c->data)[i];
+ if (str) {
+ tpl_hook.free(str);
+ ((char**)c->data)[i] = NULL;
+ }
+ }
+ tpl_hook.free(c->data);
+ find_next_node=1;
+ break;
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ case TPL_TYPE_POUND:
+ tpl_hook.free(c->data);
+ find_next_node=1;
+ break;
+ case TPL_TYPE_ARY:
+ tpl_free_atyp(c,c->data);
+ if (c->children) c = c->children; /* normal case */
+ else find_next_node=1; /* edge case, handle bad format A() */
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+
+ if (find_next_node) {
+ find_next_node=0;
+ looking=1;
+ while(looking) {
+ if (c->next) {
+ nxtc=c->next;
+ tpl_hook.free(c);
+ c=nxtc;
+ looking=0;
+ } else {
+ if (c->type == TPL_TYPE_ROOT) break; /* root node */
+ else {
+ nxtc=c->parent;
+ tpl_hook.free(c);
+ c=nxtc;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* free root */
+ for(pidx=((tpl_root_data*)(r->data))->pidx; pidx; pidx=pidx_nxt) {
+ pidx_nxt = pidx->next;
+ tpl_hook.free(pidx);
+ }
+ tpl_hook.free(((tpl_root_data*)(r->data))->fmt);
+ if (((tpl_root_data*)(r->data))->num_fxlens > 0) {
+ tpl_hook.free(((tpl_root_data*)(r->data))->fxlens);
+ }
+ tpl_hook.free(r->data); /* tpl_root_data */
+ tpl_hook.free(r);
+}
+
+
+/* Find the i'th packable ('A' node) */
+static tpl_node *tpl_find_i(tpl_node *n, int i) {
+ int j=0;
+ tpl_pidx *pidx;
+ if (n->type != TPL_TYPE_ROOT) return NULL;
+ if (i == 0) return n; /* packable 0 is root */
+ for(pidx=((tpl_root_data*)(n->data))->pidx; pidx; pidx=pidx->next) {
+ if (++j == i) return pidx->node;
+ }
+ return NULL;
+}
+
+static void *tpl_cpv(void *datav, void *data, size_t sz) {
+ if (sz>0) memcpy(datav,data,sz);
+ return (void*)((uintptr_t)datav + sz);
+}
+
+static void *tpl_extend_backbone(tpl_node *n) {
+ tpl_backbone *bb;
+ bb = (tpl_backbone*)tpl_hook.malloc(sizeof(tpl_backbone) +
+ ((tpl_atyp*)(n->data))->sz ); /* datum hangs on coattails of bb */
+ if (!bb) fatal_oom();
+#if __STDC_VERSION__ < 199901
+ bb->data = (char*)((uintptr_t)bb + sizeof(tpl_backbone));
+#endif
+ memset(bb->data,0,((tpl_atyp*)(n->data))->sz);
+ bb->next = NULL;
+ /* Add the new backbone to the tail, also setting head if necessary */
+ if (((tpl_atyp*)(n->data))->bb == NULL) {
+ ((tpl_atyp*)(n->data))->bb = bb;
+ ((tpl_atyp*)(n->data))->bbtail = bb;
+ } else {
+ ((tpl_atyp*)(n->data))->bbtail->next = bb;
+ ((tpl_atyp*)(n->data))->bbtail = bb;
+ }
+
+ ((tpl_atyp*)(n->data))->num++;
+ return bb->data;
+}
+
+/* Get the format string corresponding to a given tpl (root node) */
+static char *tpl_fmt(tpl_node *r) {
+ return ((tpl_root_data*)(r->data))->fmt;
+}
+
+/* Get the fmt # lengths as a contiguous buffer of ints (length num_fxlens) */
+static int *tpl_fxlens(tpl_node *r, int *num_fxlens) {
+ *num_fxlens = ((tpl_root_data*)(r->data))->num_fxlens;
+ return ((tpl_root_data*)(r->data))->fxlens;
+}
+
+/* called when serializing an 'A' type node into a buffer which has
+ * already been set up with the proper space. The backbone is walked
+ * which was obtained from the tpl_atyp header passed in.
+ */
+static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv) {
+ tpl_backbone *bb;
+ tpl_node *c;
+ void *datav;
+ uint32_t slen;
+ tpl_bin *binp;
+ char *strp;
+ tpl_atyp *atypp;
+ tpl_pound_data *pd;
+ int i;
+ size_t itermax;
+
+ /* handle 'A' nodes */
+ dv = tpl_cpv(dv,&at->num,sizeof(uint32_t)); /* array len */
+ for(bb=at->bb; bb; bb=bb->next) {
+ datav = bb->data;
+ c=n->children;
+ while(c) {
+ switch (c->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ dv = tpl_cpv(dv,datav,tpl_types[c->type].sz * c->num);
+ datav = (void*)((uintptr_t)datav + tpl_types[c->type].sz * c->num);
+ break;
+ case TPL_TYPE_BIN:
+ /* dump the buffer length followed by the buffer */
+ memcpy(&binp,datav,sizeof(tpl_bin*)); /* cp to aligned */
+ slen = binp->sz;
+ dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
+ dv = tpl_cpv(dv,binp->addr,slen);
+ datav = (void*)((uintptr_t)datav + sizeof(tpl_bin*));
+ break;
+ case TPL_TYPE_STR:
+ /* dump the string length followed by the string */
+ for(i=0; i < c->num; i++) {
+ memcpy(&strp,datav,sizeof(char*)); /* cp to aligned */
+ slen = strp ? (strlen(strp)+1) : 0;
+ dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
+ if (slen > 1) dv = tpl_cpv(dv,strp,slen-1);
+ datav = (void*)((uintptr_t)datav + sizeof(char*));
+ }
+ break;
+ case TPL_TYPE_ARY:
+ memcpy(&atypp,datav,sizeof(tpl_atyp*)); /* cp to aligned */
+ dv = tpl_dump_atyp(c,atypp,dv);
+ datav = (void*)((uintptr_t)datav + sizeof(void*));
+ break;
+ case TPL_TYPE_POUND:
+ /* iterate over the preceding nodes */
+ pd = (tpl_pound_data*)c->data;
+ itermax = c->num;
+ if (++(pd->iternum) < itermax) {
+ c = pd->iter_start_node;
+ continue;
+ } else { /* loop complete. */
+ pd->iternum = 0;
+ }
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+ c=c->next;
+ }
+ }
+ return dv;
+}
+
+/* figure the serialization output size needed for tpl whose root is n*/
+static size_t tpl_ser_osz(tpl_node *n) {
+ tpl_node *c, *np;
+ size_t sz, itermax;
+ tpl_bin *binp;
+ char *strp;
+ tpl_pound_data *pd;
+ int i;
+
+ /* handle the root node ONLY (subtree's ser_osz have been bubbled-up) */
+ if (n->type != TPL_TYPE_ROOT) {
+ tpl_hook.fatal("internal error: tpl_ser_osz on non-root node\n");
+ }
+
+ sz = n->ser_osz; /* start with fixed overhead, already stored */
+ c=n->children;
+ while (c) {
+ switch (c->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ sz += tpl_types[c->type].sz * c->num;
+ break;
+ case TPL_TYPE_BIN:
+ sz += sizeof(uint32_t); /* binary buf len */
+ memcpy(&binp,c->data,sizeof(tpl_bin*)); /* cp to aligned */
+ sz += binp->sz;
+ break;
+ case TPL_TYPE_STR:
+ for(i=0; i < c->num; i++) {
+ sz += sizeof(uint32_t); /* string len */
+ memcpy(&strp,&((char**)c->data)[i],sizeof(char*)); /* cp to aligned */
+ sz += strp ? strlen(strp) : 0;
+ }
+ break;
+ case TPL_TYPE_ARY:
+ sz += sizeof(uint32_t); /* array len */
+ sz += c->ser_osz; /* bubbled-up child array ser_osz */
+ break;
+ case TPL_TYPE_POUND:
+ /* iterate over the preceding nodes */
+ itermax = c->num;
+ pd = (tpl_pound_data*)c->data;
+ if (++(pd->iternum) < itermax) {
+ for(np=pd->iter_start_node; np != c; np = np->next) {
+ np->data = (char*)(np->data) +
+ (tpl_types[np->type].sz * np->num);
+ }
+ c = pd->iter_start_node;
+ continue;
+ } else { /* loop complete. */
+ pd->iternum = 0;
+ for(np=pd->iter_start_node; np != c; np = np->next) {
+ np->data = (char*)(np->data) - ((itermax-1) *
+ tpl_types[np->type].sz *
+ np->num);
+ }
+ }
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+ c=c->next;
+ }
+ return sz;
+}
+
+
+TPL_API int tpl_dump(tpl_node *r, int mode, ...) {
+ va_list ap;
+ char *filename, *bufv;
+ void **addr_out,*buf, *pa_addr;
+ int fd,rc=0;
+ size_t sz,*sz_out, pa_sz;
+
+ if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { /* unusual */
+ tpl_hook.oops("error: tpl_dump called for a loaded tpl\n");
+ return -1;
+ }
+
+ sz = tpl_ser_osz(r); /* compute the size needed to serialize */
+
+ va_start(ap,mode);
+ if (mode & TPL_FILE) {
+ filename = va_arg(ap,char*);
+ fd = tpl_mmap_output_file(filename, sz, &buf);
+ if (fd == -1) rc = -1;
+ else {
+ rc = tpl_dump_to_mem(r,buf,sz);
+ if (msync(buf,sz,MS_SYNC) == -1) {
+ tpl_hook.oops("msync failed on fd %d: %s\n", fd, strerror(errno));
+ }
+ if (munmap(buf, sz) == -1) {
+ tpl_hook.oops("munmap failed on fd %d: %s\n", fd, strerror(errno));
+ }
+ close(fd);
+ }
+ } else if (mode & TPL_FD) {
+ fd = va_arg(ap, int);
+ if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
+ tpl_dump_to_mem(r,buf,sz);
+ bufv = buf;
+ do {
+ rc = write(fd,bufv,sz);
+ if (rc > 0) {
+ sz -= rc;
+ bufv += rc;
+ } else if (rc == -1) {
+ if (errno == EINTR || errno == EAGAIN) continue;
+ tpl_hook.oops("error writing to fd %d: %s\n", fd, strerror(errno));
+ free(buf);
+ return -1;
+ }
+ } while (sz > 0);
+ free(buf);
+ rc = 0;
+ } else if (mode & TPL_MEM) {
+ if (mode & TPL_PREALLOCD) { /* caller allocated */
+ pa_addr = (void*)va_arg(ap, void*);
+ pa_sz = va_arg(ap, size_t);
+ if (pa_sz < sz) {
+ tpl_hook.oops("tpl_dump: buffer too small, need %d bytes\n", sz);
+ return -1;
+ }
+ rc=tpl_dump_to_mem(r,pa_addr,sz);
+ } else { /* we allocate */
+ addr_out = (void**)va_arg(ap, void*);
+ sz_out = va_arg(ap, size_t*);
+ if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
+ *sz_out = sz;
+ *addr_out = buf;
+ rc=tpl_dump_to_mem(r,buf,sz);
+ }
+ } else if (mode & TPL_GETSIZE) {
+ sz_out = va_arg(ap, size_t*);
+ *sz_out = sz;
+ } else {
+ tpl_hook.oops("unsupported tpl_dump mode %d\n", mode);
+ rc=-1;
+ }
+ va_end(ap);
+ return rc;
+}
+
+/* This function expects the caller to have set up a memory buffer of
+ * adequate size to hold the serialized tpl. The sz parameter must be
+ * the result of tpl_ser_osz(r).
+ */
+static int tpl_dump_to_mem(tpl_node *r,void *addr,size_t sz) {
+ uint32_t slen, sz32;
+ int *fxlens, num_fxlens, i;
+ void *dv;
+ char *fmt,flags;
+ tpl_node *c, *np;
+ tpl_pound_data *pd;
+ size_t itermax;
+
+ fmt = tpl_fmt(r);
+ flags = 0;
+ if (tpl_cpu_bigendian()) flags |= TPL_FL_BIGENDIAN;
+ if (strchr(fmt,'s')) flags |= TPL_FL_NULLSTRINGS;
+ sz32 = sz;
+
+ dv = addr;
+ dv = tpl_cpv(dv,TPL_MAGIC,3); /* copy tpl magic prefix */
+ dv = tpl_cpv(dv,&flags,1); /* copy flags byte */
+ dv = tpl_cpv(dv,&sz32,sizeof(uint32_t));/* overall length (inclusive) */
+ dv = tpl_cpv(dv,fmt,strlen(fmt)+1); /* copy format with NUL-term */
+ fxlens = tpl_fxlens(r,&num_fxlens);
+ dv = tpl_cpv(dv,fxlens,num_fxlens*sizeof(uint32_t));/* fmt # lengths */
+
+ /* serialize the tpl content, iterating over direct children of root */
+ c = r->children;
+ while (c) {
+ switch (c->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ dv = tpl_cpv(dv,c->data,tpl_types[c->type].sz * c->num);
+ break;
+ case TPL_TYPE_BIN:
+ slen = (*(tpl_bin**)(c->data))->sz;
+ dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* buffer len */
+ dv = tpl_cpv(dv,(*(tpl_bin**)(c->data))->addr,slen); /* buf */
+ break;
+ case TPL_TYPE_STR:
+ for(i=0; i < c->num; i++) {
+ char *str = ((char**)c->data)[i];
+ slen = str ? strlen(str)+1 : 0;
+ dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* string len */
+ if (slen>1) dv = tpl_cpv(dv,str,slen-1); /*string*/
+ }
+ break;
+ case TPL_TYPE_ARY:
+ dv = tpl_dump_atyp(c,(tpl_atyp*)c->data,dv);
+ break;
+ case TPL_TYPE_POUND:
+ pd = (tpl_pound_data*)c->data;
+ itermax = c->num;
+ if (++(pd->iternum) < itermax) {
+
+ /* in start or midst of loop. advance data pointers. */
+ for(np=pd->iter_start_node; np != c; np = np->next) {
+ np->data = (char*)(np->data) +
+ (tpl_types[np->type].sz * np->num);
+ }
+ /* do next iteration */
+ c = pd->iter_start_node;
+ continue;
+
+ } else { /* loop complete. */
+
+ /* reset iteration index and addr/data pointers. */
+ pd->iternum = 0;
+ for(np=pd->iter_start_node; np != c; np = np->next) {
+ np->data = (char*)(np->data) - ((itermax-1) *
+ tpl_types[np->type].sz *
+ np->num);
+ }
+
+ }
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+ c = c->next;
+ }
+
+ return 0;
+}
+
+static int tpl_cpu_bigendian() {
+ unsigned i = 1;
+ char *c;
+ c = (char*)&i;
+ return (c[0] == 1 ? 0 : 1);
+}
+
+
+/*
+ * algorithm for sanity-checking a tpl image:
+ * scan the tpl whilst not exceeding the buffer size (bufsz) ,
+ * formulating a calculated (expected) size of the tpl based
+ * on walking its data. When calcsize has been calculated it
+ * should exactly match the buffer size (bufsz) and the internal
+ * recorded size (intlsz)
+ */
+static int tpl_sanity(tpl_node *r, int excess_ok) {
+ uint32_t intlsz;
+ int found_nul=0,rc, octothorpes=0, num_fxlens, *fxlens, flen;
+ void *d, *dv;
+ char intlflags, *fmt, c, *mapfmt;
+ size_t bufsz, serlen;
+
+ d = ((tpl_root_data*)(r->data))->mmap.text;
+ bufsz = ((tpl_root_data*)(r->data))->mmap.text_sz;
+
+ dv = d;
+ if (bufsz < (4 + sizeof(uint32_t) + 1)) return ERR_NOT_MINSIZE; /* min sz: magic+flags+len+nul */
+ if (memcmp(dv,TPL_MAGIC, 3) != 0) return ERR_MAGIC_MISMATCH; /* missing tpl magic prefix */
+ if (tpl_needs_endian_swap(dv)) ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
+ dv = (void*)((uintptr_t)dv + 3);
+ memcpy(&intlflags,dv,sizeof(char)); /* extract flags */
+ if (intlflags & ~TPL_SUPPORTED_BITFLAGS) return ERR_UNSUPPORTED_FLAGS;
+ /* TPL1.3 stores strings with a "length+1" prefix to discern NULL strings from
+ empty strings from non-empty strings; TPL1.2 only handled the latter two.
+ So we need to be mindful of which string format we're reading from. */
+ if (!(intlflags & TPL_FL_NULLSTRINGS)) {
+ ((tpl_root_data*)(r->data))->flags |= TPL_OLD_STRING_FMT;
+ }
+ dv = (void*)((uintptr_t)dv + 1);
+ memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&intlsz, sizeof(uint32_t));
+ if (!excess_ok && (intlsz != bufsz)) return ERR_INCONSISTENT_SZ; /* inconsisent buffer/internal size */
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+
+ /* dv points to the start of the format string. Look for nul w/in buf sz */
+ fmt = (char*)dv;
+ while ((uintptr_t)dv-(uintptr_t)d < bufsz && !found_nul) {
+ if ( (c = *(char*)dv) != '\0') {
+ if (strchr(tpl_fmt_chars,c) == NULL)
+ return ERR_FMT_INVALID; /* invalid char in format string */
+ if ( (c = *(char*)dv) == '#') octothorpes++;
+ dv = (void*)((uintptr_t)dv + 1);
+ }
+ else found_nul = 1;
+ }
+ if (!found_nul) return ERR_FMT_MISSING_NUL; /* runaway format string */
+ dv = (void*)((uintptr_t)dv + 1); /* advance to octothorpe lengths buffer */
+
+ /* compare the map format to the format of this tpl image */
+ mapfmt = tpl_fmt(r);
+ rc = strcmp(mapfmt,fmt);
+ if (rc != 0) return ERR_FMT_MISMATCH;
+
+ /* compare octothorpe lengths in image to the mapped values */
+ if ((((uintptr_t)dv + (octothorpes * 4)) - (uintptr_t)d) > bufsz) return ERR_INCONSISTENT_SZ4;
+ fxlens = tpl_fxlens(r,&num_fxlens); /* mapped fxlens */
+ while(num_fxlens--) {
+ memcpy(&flen,dv,sizeof(uint32_t)); /* stored flen */
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&flen, sizeof(uint32_t));
+ if (flen != *fxlens) return ERR_FLEN_MISMATCH;
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ fxlens++;
+ }
+
+ /* dv now points to beginning of data */
+ rc = tpl_serlen(r,r,dv,&serlen); /* get computed serlen of data part */
+ if (rc == -1) return ERR_INCONSISTENT_SZ2; /* internal inconsistency in tpl image */
+ serlen += ((uintptr_t)dv - (uintptr_t)d); /* add back serlen of preamble part */
+ if (excess_ok && (bufsz < serlen)) return ERR_INCONSISTENT_SZ3;
+ if (!excess_ok && (serlen != bufsz)) return ERR_INCONSISTENT_SZ3; /* buffer/internal sz exceeds serlen */
+ return 0;
+}
+
+static void *tpl_find_data_start(void *d) {
+ int octothorpes=0;
+ d = (void*)((uintptr_t)d + 4); /* skip TPL_MAGIC and flags byte */
+ d = (void*)((uintptr_t)d + 4); /* skip int32 overall len */
+ while(*(char*)d != '\0') {
+ if (*(char*)d == '#') octothorpes++;
+ d = (void*)((uintptr_t)d + 1);
+ }
+ d = (void*)((uintptr_t)d + 1); /* skip NUL */
+ d = (void*)((uintptr_t)d + (octothorpes * sizeof(uint32_t))); /* skip # array lens */
+ return d;
+}
+
+static int tpl_needs_endian_swap(void *d) {
+ char *c;
+ int cpu_is_bigendian;
+ c = (char*)d;
+ cpu_is_bigendian = tpl_cpu_bigendian();
+ return ((c[3] & TPL_FL_BIGENDIAN) == cpu_is_bigendian) ? 0 : 1;
+}
+
+static size_t tpl_size_for(char c) {
+ int i;
+ for(i=0; i < sizeof(tpl_types)/sizeof(tpl_types[0]); i++) {
+ if (tpl_types[i].c == c) return tpl_types[i].sz;
+ }
+ return 0;
+}
+
+TPL_API char* tpl_peek(int mode, ...) {
+ va_list ap;
+ int xendian=0,found_nul=0,old_string_format=0;
+ char *filename=NULL, *datapeek_f=NULL, *datapeek_c, *datapeek_s;
+ void *addr=NULL, *dv, *datapeek_p=NULL;
+ size_t sz=0, fmt_len, first_atom, num_fxlens=0;
+ uint32_t datapeek_ssz, datapeek_csz, datapeek_flen;
+ tpl_mmap_rec mr = {0,NULL,0};
+ char *fmt,*fmt_cpy=NULL,c;
+ uint32_t intlsz, **fxlens=NULL, *num_fxlens_out=NULL, *fxlensv;
+
+ va_start(ap,mode);
+ if ((mode & TPL_FXLENS) && (mode & TPL_DATAPEEK)) {
+ tpl_hook.oops("TPL_FXLENS and TPL_DATAPEEK mutually exclusive\n");
+ goto fail;
+ }
+ if (mode & TPL_FILE) filename = va_arg(ap,char *);
+ else if (mode & TPL_MEM) {
+ addr = va_arg(ap,void *);
+ sz = va_arg(ap,size_t);
+ } else {
+ tpl_hook.oops("unsupported tpl_peek mode %d\n", mode);
+ goto fail;
+ }
+ if (mode & TPL_DATAPEEK) {
+ datapeek_f = va_arg(ap, char*);
+ }
+ if (mode & TPL_FXLENS) {
+ num_fxlens_out = va_arg(ap,uint32_t *);
+ fxlens = va_arg(ap,uint32_t **);
+ *num_fxlens_out = 0;
+ *fxlens = NULL;
+ }
+
+ if (mode & TPL_FILE) {
+ if (tpl_mmap_file(filename, &mr) != 0) {
+ tpl_hook.oops("tpl_peek failed for file %s\n", filename);
+ goto fail;
+ }
+ addr = mr.text;
+ sz = mr.text_sz;
+ }
+
+ dv = addr;
+ if (sz < (4 + sizeof(uint32_t) + 1)) goto fail; /* min sz */
+ if (memcmp(dv,TPL_MAGIC, 3) != 0) goto fail; /* missing tpl magic prefix */
+ if (tpl_needs_endian_swap(dv)) xendian=1;
+ if ((((char*)dv)[3] & TPL_FL_NULLSTRINGS)==0) old_string_format=1;
+ dv = (void*)((uintptr_t)dv + 4);
+ memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
+ if (xendian) tpl_byteswap(&intlsz, sizeof(uint32_t));
+ if (intlsz != sz) goto fail; /* inconsisent buffer/internal size */
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+
+ /* dv points to the start of the format string. Look for nul w/in buf sz */
+ fmt = (char*)dv;
+ while ((uintptr_t)dv-(uintptr_t)addr < sz && !found_nul) {
+ if ( (c = *(char*)dv) == '\0') {
+ found_nul = 1;
+ } else if (c == '#') {
+ num_fxlens++;
+ }
+ dv = (void*)((uintptr_t)dv + 1);
+ }
+ if (!found_nul) goto fail; /* runaway format string */
+ fmt_len = (char*)dv - fmt; /* include space for \0 */
+ fmt_cpy = tpl_hook.malloc(fmt_len);
+ if (fmt_cpy == NULL) {
+ fatal_oom();
+ }
+ memcpy(fmt_cpy, fmt, fmt_len);
+
+ /* retrieve the octothorpic lengths if requested */
+ if (num_fxlens > 0) {
+ if (sz < ((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)) - (uintptr_t)addr)) {
+ goto fail;
+ }
+ }
+ if ((mode & TPL_FXLENS) && (num_fxlens > 0)) {
+ *fxlens = tpl_hook.malloc(num_fxlens * sizeof(uint32_t));
+ if (*fxlens == NULL) tpl_hook.fatal("out of memory");
+ *num_fxlens_out = num_fxlens;
+ fxlensv = *fxlens;
+ while(num_fxlens--) {
+ memcpy(fxlensv,dv,sizeof(uint32_t));
+ if (xendian) tpl_byteswap(fxlensv, sizeof(uint32_t));
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ fxlensv++;
+ }
+ }
+ /* if caller requested, peek into the specified data elements */
+ if (mode & TPL_DATAPEEK) {
+
+ first_atom = strspn(fmt, "S()"); /* skip any leading S() */
+
+ datapeek_flen = strlen(datapeek_f);
+ if (strspn(datapeek_f, tpl_datapeek_ok_chars) < datapeek_flen) {
+ tpl_hook.oops("invalid TPL_DATAPEEK format: %s\n", datapeek_f);
+ tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
+ goto fail;
+ }
+
+ if (strncmp( &fmt[first_atom], datapeek_f, datapeek_flen) != 0) {
+ tpl_hook.oops("TPL_DATAPEEK format mismatches tpl iamge\n");
+ tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
+ goto fail;
+ }
+
+ /* advance to data start, then copy out requested elements */
+ dv = (void*)((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)));
+ for(datapeek_c = datapeek_f; *datapeek_c != '\0'; datapeek_c++) {
+ datapeek_p = va_arg(ap, void*);
+ if (*datapeek_c == 's') { /* special handling for strings */
+ if ((uintptr_t)dv-(uintptr_t)addr + sizeof(uint32_t) > sz) {
+ tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
+ tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
+ goto fail;
+ }
+ memcpy(&datapeek_ssz,dv,sizeof(uint32_t)); /* get slen */
+ if (xendian) tpl_byteswap(&datapeek_ssz, sizeof(uint32_t));
+ if (old_string_format) datapeek_ssz++;
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); /* adv. to str */
+ if (datapeek_ssz == 0) datapeek_s = NULL;
+ else {
+ if ((uintptr_t)dv-(uintptr_t)addr + datapeek_ssz-1 > sz) {
+ tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
+ tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
+ goto fail;
+ }
+ datapeek_s = tpl_hook.malloc(datapeek_ssz);
+ if (datapeek_s == NULL) fatal_oom();
+ memcpy(datapeek_s, dv, datapeek_ssz-1);
+ datapeek_s[datapeek_ssz-1] = '\0';
+ dv = (void*)((uintptr_t)dv + datapeek_ssz-1);
+ }
+ *(char**)datapeek_p = datapeek_s;
+ } else {
+ datapeek_csz = tpl_size_for(*datapeek_c);
+ if ((uintptr_t)dv-(uintptr_t)addr + datapeek_csz > sz) {
+ tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
+ tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
+ goto fail;
+ }
+ memcpy(datapeek_p, dv, datapeek_csz);
+ if (xendian) tpl_byteswap(datapeek_p, datapeek_csz);
+ dv = (void*)((uintptr_t)dv + datapeek_csz);
+ }
+ }
+ }
+
+fail:
+ va_end(ap);
+ if ((mode & TPL_FILE) && mr.text != NULL) tpl_unmap_file( &mr );
+ return fmt_cpy;
+}
+
+/* tpl_jot(TPL_FILE, "file.tpl", "si", &s, &i); */
+/* tpl_jot(TPL_MEM, &buf, &sz, "si", &s, &i); */
+/* tpl_jot(TPL_FD, fd, "si", &s, &i); */
+TPL_API int tpl_jot(int mode, ...) {
+ va_list ap;
+ char *filename, *fmt;
+ size_t *sz;
+ int fd, rc=0;
+ void **buf;
+ tpl_node *tn;
+
+ va_start(ap,mode);
+ if (mode & TPL_FILE) {
+ filename = va_arg(ap,char*);
+ fmt = va_arg(ap,char*);
+ tn = tpl_map_va(fmt, ap);
+ if (tn == NULL) { rc=-1; goto fail;}
+ tpl_pack(tn, 0);
+ rc = tpl_dump(tn, TPL_FILE, filename);
+ tpl_free(tn);
+ } else if (mode & TPL_MEM) {
+ buf = va_arg(ap,void*);
+ sz = va_arg(ap,size_t*);
+ fmt = va_arg(ap,char*);
+ tn = tpl_map_va(fmt,ap);
+ if (tn == NULL) { rc=-1; goto fail;}
+ tpl_pack(tn,0);
+ rc = tpl_dump(tn, TPL_MEM, buf, sz);
+ tpl_free(tn);
+ } else if (mode & TPL_FD) {
+ fd = va_arg(ap,int);
+ fmt = va_arg(ap,char*);
+ tn = tpl_map_va(fmt,ap);
+ if (tn == NULL) { rc=-1; goto fail;}
+ tpl_pack(tn,0);
+ rc = tpl_dump(tn, TPL_FD, fd);
+ tpl_free(tn);
+ } else {
+ tpl_hook.fatal("invalid tpl_jot mode\n");
+ }
+
+fail:
+ va_end(ap);
+ return rc;
+}
+
+TPL_API int tpl_load(tpl_node *r, int mode, ...) {
+ va_list ap;
+ int rc=0,fd=0;
+ char *filename=NULL;
+ void *addr;
+ size_t sz;
+
+ va_start(ap,mode);
+ if (mode & TPL_FILE) filename = va_arg(ap,char *);
+ else if (mode & TPL_MEM) {
+ addr = va_arg(ap,void *);
+ sz = va_arg(ap,size_t);
+ } else if (mode & TPL_FD) {
+ fd = va_arg(ap,int);
+ } else {
+ tpl_hook.oops("unsupported tpl_load mode %d\n", mode);
+ return -1;
+ }
+ va_end(ap);
+
+ if (r->type != TPL_TYPE_ROOT) {
+ tpl_hook.oops("error: tpl_load to non-root node\n");
+ return -1;
+ }
+ if (((tpl_root_data*)(r->data))->flags & (TPL_WRONLY|TPL_RDONLY)) {
+ /* already packed or loaded, so reset it as if newly mapped */
+ tpl_free_keep_map(r);
+ }
+ if (mode & TPL_FILE) {
+ if (tpl_mmap_file(filename, &((tpl_root_data*)(r->data))->mmap) != 0) {
+ tpl_hook.oops("tpl_load failed for file %s\n", filename);
+ return -1;
+ }
+ if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
+ if (rc == ERR_FMT_MISMATCH) {
+ tpl_hook.oops("%s: format signature mismatch\n", filename);
+ } else if (rc == ERR_FLEN_MISMATCH) {
+ tpl_hook.oops("%s: array lengths mismatch\n", filename);
+ } else {
+ tpl_hook.oops("%s: not a valid tpl file\n", filename);
+ }
+ tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap );
+ return -1;
+ }
+ ((tpl_root_data*)(r->data))->flags = (TPL_FILE | TPL_RDONLY);
+ } else if (mode & TPL_MEM) {
+ ((tpl_root_data*)(r->data))->mmap.text = addr;
+ ((tpl_root_data*)(r->data))->mmap.text_sz = sz;
+ if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
+ if (rc == ERR_FMT_MISMATCH) {
+ tpl_hook.oops("format signature mismatch\n");
+ } else {
+ tpl_hook.oops("not a valid tpl file\n");
+ }
+ return -1;
+ }
+ ((tpl_root_data*)(r->data))->flags = (TPL_MEM | TPL_RDONLY);
+ if (mode & TPL_UFREE) ((tpl_root_data*)(r->data))->flags |= TPL_UFREE;
+ } else if (mode & TPL_FD) {
+ /* if fd read succeeds, resulting mem img is used for load */
+ if (tpl_gather(TPL_GATHER_BLOCKING,fd,&addr,&sz) > 0) {
+ return tpl_load(r, TPL_MEM|TPL_UFREE, addr, sz);
+ } else return -1;
+ } else {
+ tpl_hook.oops("invalid tpl_load mode %d\n", mode);
+ return -1;
+ }
+ /* this applies to TPL_MEM or TPL_FILE */
+ if (tpl_needs_endian_swap(((tpl_root_data*)(r->data))->mmap.text))
+ ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
+ tpl_unpackA0(r); /* prepare root A nodes for use */
+ return 0;
+}
+
+TPL_API int tpl_Alen(tpl_node *r, int i) {
+ tpl_node *n;
+
+ n = tpl_find_i(r,i);
+ if (n == NULL) {
+ tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
+ return -1;
+ }
+ if (n->type != TPL_TYPE_ARY) return -1;
+ return ((tpl_atyp*)(n->data))->num;
+}
+
+static void tpl_free_atyp(tpl_node *n, tpl_atyp *atyp) {
+ tpl_backbone *bb,*bbnxt;
+ tpl_node *c;
+ void *dv;
+ tpl_bin *binp;
+ tpl_atyp *atypp;
+ char *strp;
+ size_t itermax;
+ tpl_pound_data *pd;
+ int i;
+
+ bb = atyp->bb;
+ while (bb) {
+ bbnxt = bb->next;
+ dv = bb->data;
+ c=n->children;
+ while (c) {
+ switch (c->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz*c->num);
+ break;
+ case TPL_TYPE_BIN:
+ memcpy(&binp,dv,sizeof(tpl_bin*)); /* cp to aligned */
+ if (binp->addr) tpl_hook.free( binp->addr ); /* free buf */
+ tpl_hook.free(binp); /* free tpl_bin */
+ dv = (void*)((uintptr_t)dv + sizeof(tpl_bin*));
+ break;
+ case TPL_TYPE_STR:
+ for(i=0; i < c->num; i++) {
+ memcpy(&strp,dv,sizeof(char*)); /* cp to aligned */
+ if (strp) tpl_hook.free(strp); /* free string */
+ dv = (void*)((uintptr_t)dv + sizeof(char*));
+ }
+ break;
+ case TPL_TYPE_POUND:
+ /* iterate over the preceding nodes */
+ itermax = c->num;
+ pd = (tpl_pound_data*)c->data;
+ if (++(pd->iternum) < itermax) {
+ c = pd->iter_start_node;
+ continue;
+ } else { /* loop complete. */
+ pd->iternum = 0;
+ }
+ break;
+ case TPL_TYPE_ARY:
+ memcpy(&atypp,dv,sizeof(tpl_atyp*)); /* cp to aligned */
+ tpl_free_atyp(c,atypp); /* free atyp */
+ dv = (void*)((uintptr_t)dv + sizeof(void*));
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+ c=c->next;
+ }
+ tpl_hook.free(bb);
+ bb = bbnxt;
+ }
+ tpl_hook.free(atyp);
+}
+
+/* determine (by walking) byte length of serialized r/A node at address dv
+ * returns 0 on success, or -1 if the tpl isn't trustworthy (fails consistency)
+ */
+static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen) {
+ uint32_t slen;
+ int num,fidx;
+ tpl_node *c;
+ size_t len=0, alen, buf_past, itermax;
+ tpl_pound_data *pd;
+
+ buf_past = ((uintptr_t)((tpl_root_data*)(r->data))->mmap.text +
+ ((tpl_root_data*)(r->data))->mmap.text_sz);
+
+ if (n->type == TPL_TYPE_ROOT) num = 1;
+ else if (n->type == TPL_TYPE_ARY) {
+ if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
+ memcpy(&num,dv,sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&num, sizeof(uint32_t));
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ len += sizeof(uint32_t);
+ } else tpl_hook.fatal("internal error in tpl_serlen\n");
+
+ while (num-- > 0) {
+ c=n->children;
+ while (c) {
+ switch (c->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
+ if ((uintptr_t)dv + tpl_types[c->type].sz > buf_past) return -1;
+ dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
+ len += tpl_types[c->type].sz;
+ }
+ break;
+ case TPL_TYPE_BIN:
+ len += sizeof(uint32_t);
+ if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
+ memcpy(&slen,dv,sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&slen, sizeof(uint32_t));
+ len += slen;
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ if ((uintptr_t)dv + slen > buf_past) return -1;
+ dv = (void*)((uintptr_t)dv + slen);
+ break;
+ case TPL_TYPE_STR:
+ for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
+ len += sizeof(uint32_t);
+ if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
+ memcpy(&slen,dv,sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&slen, sizeof(uint32_t));
+ if (!(((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT))
+ slen = (slen>1) ? (slen-1) : 0;
+ len += slen;
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ if ((uintptr_t)dv + slen > buf_past) return -1;
+ dv = (void*)((uintptr_t)dv + slen);
+ }
+ break;
+ case TPL_TYPE_ARY:
+ if ( tpl_serlen(r,c,dv, &alen) == -1) return -1;
+ dv = (void*)((uintptr_t)dv + alen);
+ len += alen;
+ break;
+ case TPL_TYPE_POUND:
+ /* iterate over the preceding nodes */
+ itermax = c->num;
+ pd = (tpl_pound_data*)c->data;
+ if (++(pd->iternum) < itermax) {
+ c = pd->iter_start_node;
+ continue;
+ } else { /* loop complete. */
+ pd->iternum = 0;
+ }
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+ c=c->next;
+ }
+ }
+ *serlen = len;
+ return 0;
+}
+
+static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out) {
+ void *text;
+ int fd,perms;
+
+#ifndef _WIN32
+ perms = S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH; /* ug+w o+r */
+ fd=open(filename,O_CREAT|O_TRUNC|O_RDWR,perms);
+#else
+ perms = _S_IWRITE;
+ fd=_open(filename,_O_CREAT|_O_TRUNC|_O_RDWR,perms);
+#endif
+
+ if ( fd == -1 ) {
+ tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ text = mmap(0, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (text == MAP_FAILED) {
+ tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ if (ftruncate(fd,sz) == -1) {
+ tpl_hook.oops("ftruncate failed: %s\n", strerror(errno));
+ munmap( text, sz );
+ close(fd);
+ return -1;
+ }
+ *text_out = text;
+ return fd;
+}
+
+static int tpl_mmap_file(char *filename, tpl_mmap_rec *mr) {
+ struct stat stat_buf;
+
+ if ( (mr->fd = open(filename, O_RDONLY)) == -1 ) {
+ tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ if ( fstat(mr->fd, &stat_buf) == -1) {
+ close(mr->fd);
+ tpl_hook.oops("Couldn't stat file %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ mr->text_sz = (size_t)stat_buf.st_size;
+ mr->text = mmap(0, stat_buf.st_size, PROT_READ, MAP_PRIVATE, mr->fd, 0);
+ if (mr->text == MAP_FAILED) {
+ close(mr->fd);
+ tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+TPL_API int tpl_pack(tpl_node *r, int i) {
+ tpl_node *n, *child, *np;
+ void *datav=NULL;
+ size_t sz, itermax;
+ uint32_t slen;
+ char *str;
+ tpl_bin *bin;
+ tpl_pound_data *pd;
+ int fidx;
+
+ n = tpl_find_i(r,i);
+ if (n == NULL) {
+ tpl_hook.oops("invalid index %d to tpl_pack\n", i);
+ return -1;
+ }
+
+ if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) {
+ /* convert to an writeable tpl, initially empty */
+ tpl_free_keep_map(r);
+ }
+
+ ((tpl_root_data*)(r->data))->flags |= TPL_WRONLY;
+
+ if (n->type == TPL_TYPE_ARY) datav = tpl_extend_backbone(n);
+ child = n->children;
+ while(child) {
+ switch(child->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ /* no need to use fidx iteration here; we can copy multiple values in one memcpy */
+ memcpy(child->data,child->addr,tpl_types[child->type].sz * child->num);
+ if (datav) datav = tpl_cpv(datav,child->data,tpl_types[child->type].sz * child->num);
+ if (n->type == TPL_TYPE_ARY) n->ser_osz += tpl_types[child->type].sz * child->num;
+ break;
+ case TPL_TYPE_BIN:
+ /* copy the buffer to be packed */
+ slen = ((tpl_bin*)child->addr)->sz;
+ if (slen >0) {
+ str = tpl_hook.malloc(slen);
+ if (!str) fatal_oom();
+ memcpy(str,((tpl_bin*)child->addr)->addr,slen);
+ } else str = NULL;
+ /* and make a tpl_bin to point to it */
+ bin = tpl_hook.malloc(sizeof(tpl_bin));
+ if (!bin) fatal_oom();
+ bin->addr = str;
+ bin->sz = slen;
+ /* now pack its pointer, first deep freeing any pre-existing bin */
+ if (*(tpl_bin**)(child->data) != NULL) {
+ if ((*(tpl_bin**)(child->data))->sz != 0) {
+ tpl_hook.free( (*(tpl_bin**)(child->data))->addr );
+ }
+ tpl_hook.free(*(tpl_bin**)(child->data));
+ }
+ memcpy(child->data,&bin,sizeof(tpl_bin*));
+ if (datav) {
+ datav = tpl_cpv(datav, &bin, sizeof(tpl_bin*));
+ *(tpl_bin**)(child->data) = NULL;
+ }
+ if (n->type == TPL_TYPE_ARY) {
+ n->ser_osz += sizeof(uint32_t); /* binary buf len word */
+ n->ser_osz += bin->sz; /* binary buf */
+ }
+ break;
+ case TPL_TYPE_STR:
+ for(fidx=0; fidx < child->num; fidx++) {
+ /* copy the string to be packed. slen includes \0. this
+ block also works if the string pointer is NULL. */
+ char *caddr = ((char**)child->addr)[fidx];
+ char **cdata = &((char**)child->data)[fidx];
+ slen = caddr ? (strlen(caddr) + 1) : 0;
+ if (slen) {
+ str = tpl_hook.malloc(slen);
+ if (!str) fatal_oom();
+ memcpy(str,caddr,slen); /* include \0 */
+ } else {
+ str = NULL;
+ }
+ /* now pack its pointer, first freeing any pre-existing string */
+ if (*cdata != NULL) {
+ tpl_hook.free(*cdata);
+ }
+ memcpy(cdata,&str,sizeof(char*));
+ if (datav) {
+ datav = tpl_cpv(datav, &str, sizeof(char*));
+ *cdata = NULL;
+ }
+ if (n->type == TPL_TYPE_ARY) {
+ n->ser_osz += sizeof(uint32_t); /* string len word */
+ if (slen>1) n->ser_osz += slen-1;/* string (without nul) */
+ }
+ }
+ break;
+ case TPL_TYPE_ARY:
+ /* copy the child's tpl_atype* and reset it to empty */
+ if (datav) {
+ sz = ((tpl_atyp*)(child->data))->sz;
+ datav = tpl_cpv(datav, &child->data, sizeof(void*));
+ child->data = tpl_hook.malloc(sizeof(tpl_atyp));
+ if (!child->data) fatal_oom();
+ ((tpl_atyp*)(child->data))->num = 0;
+ ((tpl_atyp*)(child->data))->sz = sz;
+ ((tpl_atyp*)(child->data))->bb = NULL;
+ ((tpl_atyp*)(child->data))->bbtail = NULL;
+ }
+ /* parent is array? then bubble up child array's ser_osz */
+ if (n->type == TPL_TYPE_ARY) {
+ n->ser_osz += sizeof(uint32_t); /* array len word */
+ n->ser_osz += child->ser_osz; /* child array ser_osz */
+ child->ser_osz = 0; /* reset child array ser_osz */
+ }
+ break;
+
+ case TPL_TYPE_POUND:
+ /* we need to iterate n times over preceding nodes in S(...).
+ * we may be in the midst of an iteration each time or starting. */
+ pd = (tpl_pound_data*)child->data;
+ itermax = child->num;
+
+ /* itermax is total num of iterations needed */
+ /* pd->iternum is current iteration index */
+ /* pd->inter_elt_len is element-to-element len of contiguous structs */
+ /* pd->iter_start_node is where we jump to at each iteration. */
+
+ if (++(pd->iternum) < itermax) {
+
+ /* in start or midst of loop. advance addr/data pointers. */
+ for(np=pd->iter_start_node; np != child; np = np->next) {
+ np->data = (char*)(np->data) +
+ (tpl_types[np->type].sz * np->num);
+ np->addr = (char*)(np->addr) + pd->inter_elt_len;
+ }
+ /* do next iteration */
+ child = pd->iter_start_node;
+ continue;
+
+ } else { /* loop complete. */
+
+ /* reset iteration index and addr/data pointers. */
+ pd->iternum = 0;
+ for(np=pd->iter_start_node; np != child; np = np->next) {
+ np->data = (char*)(np->data) - ((itermax-1) *
+ tpl_types[np->type].sz *
+ np->num);
+ np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
+ }
+
+ }
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+ child=child->next;
+ }
+ return 0;
+}
+
+TPL_API int tpl_unpack(tpl_node *r, int i) {
+ tpl_node *n, *c, *np;
+ uint32_t slen;
+ int rc=1, fidx;
+ char *str;
+ void *dv=NULL, *caddr;
+ size_t A_bytes, itermax;
+ tpl_pound_data *pd;
+ void *img;
+ size_t sz;
+
+
+ /* handle unusual case of tpl_pack,tpl_unpack without an
+ * intervening tpl_dump. do a dump/load implicitly. */
+ if (((tpl_root_data*)(r->data))->flags & TPL_WRONLY) {
+ if (tpl_dump(r,TPL_MEM,&img,&sz) != 0) return -1;
+ if (tpl_load(r,TPL_MEM|TPL_UFREE,img,sz) != 0) {
+ tpl_hook.free(img);
+ return -1;
+ };
+ }
+
+ n = tpl_find_i(r,i);
+ if (n == NULL) {
+ tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
+ return -1;
+ }
+
+ /* either root node or an A node */
+ if (n->type == TPL_TYPE_ROOT) {
+ dv = tpl_find_data_start( ((tpl_root_data*)(n->data))->mmap.text );
+ } else if (n->type == TPL_TYPE_ARY) {
+ if (((tpl_atyp*)(n->data))->num <= 0) return 0; /* array consumed */
+ else rc = ((tpl_atyp*)(n->data))->num--;
+ dv = ((tpl_atyp*)(n->data))->cur;
+ if (!dv) tpl_hook.fatal("must unpack parent of node before node itself\n");
+ }
+
+ c = n->children;
+ while (c) {
+ switch (c->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ /* unpack elements of cross-endian octothorpic array individually */
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) {
+ for(fidx=0; fidx < c->num; fidx++) {
+ caddr = (void*)((uintptr_t)c->addr + (fidx * tpl_types[c->type].sz));
+ memcpy(caddr,dv,tpl_types[c->type].sz);
+ tpl_byteswap(caddr, tpl_types[c->type].sz);
+ dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
+ }
+ } else {
+ /* bulk unpack ok if not cross-endian */
+ memcpy(c->addr, dv, tpl_types[c->type].sz * c->num);
+ dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz * c->num);
+ }
+ break;
+ case TPL_TYPE_BIN:
+ memcpy(&slen,dv,sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&slen, sizeof(uint32_t));
+ if (slen > 0) {
+ str = (char*)tpl_hook.malloc(slen);
+ if (!str) fatal_oom();
+ } else str=NULL;
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ if (slen>0) memcpy(str,dv,slen);
+ memcpy(&(((tpl_bin*)c->addr)->addr),&str,sizeof(void*));
+ memcpy(&(((tpl_bin*)c->addr)->sz),&slen,sizeof(uint32_t));
+ dv = (void*)((uintptr_t)dv + slen);
+ break;
+ case TPL_TYPE_STR:
+ for(fidx=0; fidx < c->num; fidx++) {
+ memcpy(&slen,dv,sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&slen, sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
+ slen += 1;
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ if (slen) { /* slen includes \0 */
+ str = (char*)tpl_hook.malloc(slen);
+ if (!str) fatal_oom();
+ if (slen>1) memcpy(str,dv,slen-1);
+ str[slen-1] = '\0'; /* nul terminate */
+ dv = (void*)((uintptr_t)dv + slen-1);
+ } else str=NULL;
+ memcpy(&((char**)c->addr)[fidx],&str,sizeof(char*));
+ }
+ break;
+ case TPL_TYPE_POUND:
+ /* iterate over preceding nodes */
+ pd = (tpl_pound_data*)c->data;
+ itermax = c->num;
+ if (++(pd->iternum) < itermax) {
+ /* in start or midst of loop. advance addr/data pointers. */
+ for(np=pd->iter_start_node; np != c; np = np->next) {
+ np->addr = (char*)(np->addr) + pd->inter_elt_len;
+ }
+ /* do next iteration */
+ c = pd->iter_start_node;
+ continue;
+
+ } else { /* loop complete. */
+
+ /* reset iteration index and addr/data pointers. */
+ pd->iternum = 0;
+ for(np=pd->iter_start_node; np != c; np = np->next) {
+ np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
+ }
+
+ }
+ break;
+ case TPL_TYPE_ARY:
+ if (tpl_serlen(r,c,dv, &A_bytes) == -1)
+ tpl_hook.fatal("internal error in unpack\n");
+ memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
+ ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
+ dv = (void*)((uintptr_t)dv + A_bytes);
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+
+ c = c->next;
+ }
+ if (n->type == TPL_TYPE_ARY) ((tpl_atyp*)(n->data))->cur = dv; /* next element */
+ return rc;
+}
+
+/* Specialized function that unpacks only the root's A nodes, after tpl_load */
+static int tpl_unpackA0(tpl_node *r) {
+ tpl_node *n, *c;
+ uint32_t slen;
+ int rc=1,fidx,i;
+ void *dv;
+ size_t A_bytes, itermax;
+ tpl_pound_data *pd;
+
+ n = r;
+ dv = tpl_find_data_start( ((tpl_root_data*)(r->data))->mmap.text);
+
+ c=n->children;
+ while (c) {
+ switch (c->type) {
+ case TPL_TYPE_BYTE:
+ case TPL_TYPE_DOUBLE:
+ case TPL_TYPE_INT32:
+ case TPL_TYPE_UINT32:
+ case TPL_TYPE_INT64:
+ case TPL_TYPE_UINT64:
+ case TPL_TYPE_INT16:
+ case TPL_TYPE_UINT16:
+ for(fidx=0;fidx < c->num; fidx++) {
+ dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
+ }
+ break;
+ case TPL_TYPE_BIN:
+ memcpy(&slen,dv,sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&slen, sizeof(uint32_t));
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ dv = (void*)((uintptr_t)dv + slen);
+ break;
+ case TPL_TYPE_STR:
+ for(i=0; i<c->num; i++) {
+ memcpy(&slen,dv,sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&slen, sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
+ slen += 1;
+ dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
+ if (slen>1) dv = (void*)((uintptr_t)dv + slen-1);
+ }
+ break;
+ case TPL_TYPE_POUND:
+ /* iterate over the preceding nodes */
+ itermax = c->num;
+ pd = (tpl_pound_data*)c->data;
+ if (++(pd->iternum) < itermax) {
+ c = pd->iter_start_node;
+ continue;
+ } else { /* loop complete. */
+ pd->iternum = 0;
+ }
+ break;
+ case TPL_TYPE_ARY:
+ if ( tpl_serlen(r,c,dv, &A_bytes) == -1)
+ tpl_hook.fatal("internal error in unpackA0\n");
+ memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
+ if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
+ tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
+ ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
+ dv = (void*)((uintptr_t)dv + A_bytes);
+ break;
+ default:
+ tpl_hook.fatal("unsupported format character\n");
+ break;
+ }
+ c=c->next;
+ }
+ return rc;
+}
+
+/* In-place byte order swapping of a word of length "len" bytes */
+static void tpl_byteswap(void *word, int len) {
+ int i;
+ char c, *w;
+ w = (char*)word;
+ for(i=0; i<len/2; i++) {
+ c = w[i];
+ w[i] = w[len-1-i];
+ w[len-1-i] = c;
+ }
+}
+
+static void tpl_fatal(char *fmt, ...) {
+ va_list ap;
+ char exit_msg[100];
+
+ va_start(ap,fmt);
+ vsnprintf(exit_msg, 100, fmt, ap);
+ va_end(ap);
+
+ tpl_hook.oops("%s", exit_msg);
+ exit(-1);
+}
+
+TPL_API int tpl_gather(int mode, ...) {
+ va_list ap;
+ int fd,rc=0;
+ size_t *szp,sz;
+ void **img,*addr,*data;
+ tpl_gather_t **gs;
+ tpl_gather_cb *cb;
+
+ va_start(ap,mode);
+ switch (mode) {
+ case TPL_GATHER_BLOCKING:
+ fd = va_arg(ap,int);
+ img = va_arg(ap,void*);
+ szp = va_arg(ap,size_t*);
+ rc = tpl_gather_blocking(fd,img,szp);
+ break;
+ case TPL_GATHER_NONBLOCKING:
+ fd = va_arg(ap,int);
+ gs = (tpl_gather_t**)va_arg(ap,void*);
+ cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
+ data = va_arg(ap,void*);
+ rc = tpl_gather_nonblocking(fd,gs,cb,data);
+ break;
+ case TPL_GATHER_MEM:
+ addr = va_arg(ap,void*);
+ sz = va_arg(ap,size_t);
+ gs = (tpl_gather_t**)va_arg(ap,void*);
+ cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
+ data = va_arg(ap,void*);
+ rc = tpl_gather_mem(addr,sz,gs,cb,data);
+ break;
+ default:
+ tpl_hook.fatal("unsupported tpl_gather mode %d\n",mode);
+ break;
+ }
+ va_end(ap);
+ return rc;
+}
+
+/* dequeue a tpl by reading until one full tpl image is obtained.
+ * We take care not to read past the end of the tpl.
+ * This is intended as a blocking call i.e. for use with a blocking fd.
+ * It can be given a non-blocking fd, but the read spins if we have to wait.
+ */
+static int tpl_gather_blocking(int fd, void **img, size_t *sz) {
+ char preamble[8];
+ int i=0, rc;
+ uint32_t tpllen;
+
+ do {
+ rc = read(fd,&preamble[i],8-i);
+ i += (rc>0) ? rc : 0;
+ } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<8));
+
+ if (rc<0) {
+ tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
+ return -1;
+ } else if (rc == 0) {
+ /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
+ return 0;
+ } else if (i != 8) {
+ tpl_hook.oops("internal error\n");
+ return -1;
+ }
+
+ if (preamble[0] == 't' && preamble[1] == 'p' && preamble[2] == 'l') {
+ memcpy(&tpllen,&preamble[4],4);
+ if (tpl_needs_endian_swap(preamble)) tpl_byteswap(&tpllen,4);
+ } else {
+ tpl_hook.oops("tpl_gather_fd_blocking: non-tpl input\n");
+ return -1;
+ }
+
+ /* malloc space for remainder of tpl image (overall length tpllen)
+ * and read it in
+ */
+ if (tpl_hook.gather_max > 0 &&
+ tpllen > tpl_hook.gather_max) {
+ tpl_hook.oops("tpl exceeds max length %d\n",
+ tpl_hook.gather_max);
+ return -2;
+ }
+ *sz = tpllen;
+ if ( (*img = tpl_hook.malloc(tpllen)) == NULL) {
+ fatal_oom();
+ }
+
+ memcpy(*img,preamble,8); /* copy preamble to output buffer */
+ i=8;
+ do {
+ rc = read(fd,&((*(char**)img)[i]),tpllen-i);
+ i += (rc>0) ? rc : 0;
+ } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<tpllen));
+
+ if (rc<0) {
+ tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
+ tpl_hook.free(*img);
+ return -1;
+ } else if (rc == 0) {
+ /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
+ tpl_hook.free(*img);
+ return 0;
+ } else if (i != tpllen) {
+ tpl_hook.oops("internal error\n");
+ tpl_hook.free(*img);
+ return -1;
+ }
+
+ return 1;
+}
+
+/* Used by select()-driven apps which want to gather tpl images piecemeal */
+/* the file descriptor must be non-blocking for this functino to work. */
+static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
+ char buf[TPL_GATHER_BUFLEN], *img, *tpl;
+ int rc, keep_looping, cbrc=0;
+ size_t catlen;
+ uint32_t tpllen;
+
+ while (1) {
+ rc = read(fd,buf,TPL_GATHER_BUFLEN);
+ if (rc == -1) {
+ if (errno == EINTR) continue; /* got signal during read, ignore */
+ if (errno == EAGAIN) return 1; /* nothing to read right now */
+ else {
+ tpl_hook.oops("tpl_gather failed: %s\n", strerror(errno));
+ if (*gs) {
+ tpl_hook.free((*gs)->img);
+ tpl_hook.free(*gs);
+ *gs = NULL;
+ }
+ return -1; /* error, caller should close fd */
+ }
+ } else if (rc == 0) {
+ if (*gs) {
+ tpl_hook.oops("tpl_gather: partial tpl image precedes EOF\n");
+ tpl_hook.free((*gs)->img);
+ tpl_hook.free(*gs);
+ *gs = NULL;
+ }
+ return 0; /* EOF, caller should close fd */
+ } else {
+ /* concatenate any partial tpl from last read with new buffer */
+ if (*gs) {
+ catlen = (*gs)->len + rc;
+ if (tpl_hook.gather_max > 0 &&
+ catlen > tpl_hook.gather_max) {
+ tpl_hook.free( (*gs)->img );
+ tpl_hook.free( (*gs) );
+ *gs = NULL;
+ tpl_hook.oops("tpl exceeds max length %d\n",
+ tpl_hook.gather_max);
+ return -2; /* error, caller should close fd */
+ }
+ if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
+ fatal_oom();
+ }
+ memcpy(img + (*gs)->len, buf, rc);
+ tpl_hook.free(*gs);
+ *gs = NULL;
+ } else {
+ img = buf;
+ catlen = rc;
+ }
+ /* isolate any full tpl(s) in img and invoke cb for each */
+ tpl = img;
+ keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
+ while (keep_looping) {
+ if (strncmp("tpl", tpl, 3) != 0) {
+ tpl_hook.oops("tpl prefix invalid\n");
+ if (img != buf) tpl_hook.free(img);
+ tpl_hook.free(*gs);
+ *gs = NULL;
+ return -3; /* error, caller should close fd */
+ }
+ memcpy(&tpllen,&tpl[4],4);
+ if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
+ if (tpl+tpllen <= img+catlen) {
+ cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
+ tpl += tpllen; /* point to next tpl image */
+ if (cbrc < 0) keep_looping = 0;
+ else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
+ } else keep_looping=0;
+ }
+ /* check if app callback requested closure of tpl source */
+ if (cbrc < 0) {
+ tpl_hook.oops("tpl_fd_gather aborted by app callback\n");
+ if (img != buf) tpl_hook.free(img);
+ if (*gs) tpl_hook.free(*gs);
+ *gs = NULL;
+ return -4;
+ }
+ /* store any leftover, partial tpl fragment for next read */
+ if (tpl == img && img != buf) {
+ /* consumed nothing from img!=buf */
+ if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
+ fatal_oom();
+ }
+ (*gs)->img = tpl;
+ (*gs)->len = catlen;
+ } else if (tpl < img+catlen) {
+ /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
+ if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
+ fatal_oom();
+ }
+ if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
+ fatal_oom();
+ }
+ (*gs)->len = img+catlen - tpl;
+ memcpy( (*gs)->img, tpl, img+catlen - tpl);
+ /* free partially consumed concat buffer if used */
+ if (img != buf) tpl_hook.free(img);
+ } else { /* tpl(s) fully consumed */
+ /* free consumed concat buffer if used */
+ if (img != buf) tpl_hook.free(img);
+ }
+ }
+ }
+}
+
+/* gather tpl piecemeal from memory buffer (not fd) e.g., from a lower-level api */
+static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
+ char *img, *tpl;
+ int keep_looping, cbrc=0;
+ size_t catlen;
+ uint32_t tpllen;
+
+ /* concatenate any partial tpl from last read with new buffer */
+ if (*gs) {
+ catlen = (*gs)->len + len;
+ if (tpl_hook.gather_max > 0 &&
+ catlen > tpl_hook.gather_max) {
+ tpl_hook.free( (*gs)->img );
+ tpl_hook.free( (*gs) );
+ *gs = NULL;
+ tpl_hook.oops("tpl exceeds max length %d\n",
+ tpl_hook.gather_max);
+ return -2; /* error, caller should stop accepting input from source*/
+ }
+ if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
+ fatal_oom();
+ }
+ memcpy(img + (*gs)->len, buf, len);
+ tpl_hook.free(*gs);
+ *gs = NULL;
+ } else {
+ img = buf;
+ catlen = len;
+ }
+ /* isolate any full tpl(s) in img and invoke cb for each */
+ tpl = img;
+ keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
+ while (keep_looping) {
+ if (strncmp("tpl", tpl, 3) != 0) {
+ tpl_hook.oops("tpl prefix invalid\n");
+ if (img != buf) tpl_hook.free(img);
+ tpl_hook.free(*gs);
+ *gs = NULL;
+ return -3; /* error, caller should stop accepting input from source*/
+ }
+ memcpy(&tpllen,&tpl[4],4);
+ if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
+ if (tpl+tpllen <= img+catlen) {
+ cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
+ tpl += tpllen; /* point to next tpl image */
+ if (cbrc < 0) keep_looping = 0;
+ else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
+ } else keep_looping=0;
+ }
+ /* check if app callback requested closure of tpl source */
+ if (cbrc < 0) {
+ tpl_hook.oops("tpl_mem_gather aborted by app callback\n");
+ if (img != buf) tpl_hook.free(img);
+ if (*gs) tpl_hook.free(*gs);
+ *gs = NULL;
+ return -4;
+ }
+ /* store any leftover, partial tpl fragment for next read */
+ if (tpl == img && img != buf) {
+ /* consumed nothing from img!=buf */
+ if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
+ fatal_oom();
+ }
+ (*gs)->img = tpl;
+ (*gs)->len = catlen;
+ } else if (tpl < img+catlen) {
+ /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
+ if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
+ fatal_oom();
+ }
+ if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
+ fatal_oom();
+ }
+ (*gs)->len = img+catlen - tpl;
+ memcpy( (*gs)->img, tpl, img+catlen - tpl);
+ /* free partially consumed concat buffer if used */
+ if (img != buf) tpl_hook.free(img);
+ } else { /* tpl(s) fully consumed */
+ /* free consumed concat buffer if used */
+ if (img != buf) tpl_hook.free(img);
+ }
+ return 1;
+}
--- /dev/null
+/*
+Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef TPL_H
+#define TPL_H
+
+#include <stddef.h> /* size_t */
+
+#ifdef __INTEL_COMPILER
+#include <tbb/tbbmalloc_proxy.h>
+#endif /* Intel Compiler efficient memcpy etc */
+
+#ifdef _MSC_VER
+typedef unsigned int uint32_t;
+#else
+#include <inttypes.h> /* uint32_t */
+#endif
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#ifdef TPL_EXPORTS
+#define TPL_API __declspec(dllexport)
+#else /* */
+#ifdef TPL_NOLIB
+#define TPL_API
+#else
+#define TPL_API __declspec(dllimport)
+#endif /* TPL_NOLIB */
+#endif /* TPL_EXPORTS*/
+#else
+#define TPL_API
+#endif
+
+/* bit flags (external) */
+#define TPL_FILE (1 << 0)
+#define TPL_MEM (1 << 1)
+#define TPL_PREALLOCD (1 << 2)
+#define TPL_EXCESS_OK (1 << 3)
+#define TPL_FD (1 << 4)
+#define TPL_UFREE (1 << 5)
+#define TPL_DATAPEEK (1 << 6)
+#define TPL_FXLENS (1 << 7)
+#define TPL_GETSIZE (1 << 8)
+/* do not add flags here without renumbering the internal flags! */
+
+/* flags for tpl_gather mode */
+#define TPL_GATHER_BLOCKING 1
+#define TPL_GATHER_NONBLOCKING 2
+#define TPL_GATHER_MEM 3
+
+/* Hooks for error logging, memory allocation functions and fatal */
+typedef int (tpl_print_fcn)(const char *fmt, ...);
+typedef void *(tpl_malloc_fcn)(size_t sz);
+typedef void *(tpl_realloc_fcn)(void *ptr, size_t sz);
+typedef void (tpl_free_fcn)(void *ptr);
+typedef void (tpl_fatal_fcn)(char *fmt, ...);
+
+typedef struct tpl_hook_t {
+ tpl_print_fcn *oops;
+ tpl_malloc_fcn *malloc;
+ tpl_realloc_fcn *realloc;
+ tpl_free_fcn *free;
+ tpl_fatal_fcn *fatal;
+ size_t gather_max;
+} tpl_hook_t;
+
+typedef struct tpl_node {
+ int type;
+ void *addr;
+ void *data; /* r:tpl_root_data*. A:tpl_atyp*. ow:szof type */
+ int num; /* length of type if its a C array */
+ size_t ser_osz; /* serialization output size for subtree */
+ struct tpl_node *children; /* my children; linked-list */
+ struct tpl_node *next,*prev; /* my siblings (next child of my parent) */
+ struct tpl_node *parent; /* my parent */
+} tpl_node;
+
+/* used when un/packing 'B' type (binary buffers) */
+typedef struct tpl_bin {
+ void *addr;
+ uint32_t sz;
+} tpl_bin;
+
+/* for async/piecemeal reading of tpl images */
+typedef struct tpl_gather_t {
+ char *img;
+ int len;
+} tpl_gather_t;
+
+/* Callback used when tpl_gather has read a full tpl image */
+typedef int (tpl_gather_cb)(void *img, size_t sz, void *data);
+
+/* Prototypes */
+TPL_API tpl_node *tpl_map(char *fmt,...); /* define tpl using format */
+TPL_API void tpl_free(tpl_node *r); /* free a tpl map */
+TPL_API int tpl_pack(tpl_node *r, int i); /* pack the n'th packable */
+TPL_API int tpl_unpack(tpl_node *r, int i); /* unpack the n'th packable */
+TPL_API int tpl_dump(tpl_node *r, int mode, ...); /* serialize to mem/file */
+TPL_API int tpl_load(tpl_node *r, int mode, ...); /* set mem/file to unpack */
+TPL_API int tpl_Alen(tpl_node *r, int i); /* array len of packable i */
+TPL_API char* tpl_peek(int mode, ...); /* sneak peek at format string */
+TPL_API int tpl_gather( int mode, ...); /* non-blocking image gather */
+TPL_API int tpl_jot(int mode, ...); /* quick write a simple tpl */
+
+#if defined __cplusplus
+ }
+#endif
+
+#endif /* TPL_H */
+
--- /dev/null
+noinst_LTLIBRARIES = libwinmmap.la
+noinst_HEADERS = mman.h
+libwinmmap_la_SOURCES = nonempty.c
+libwinmmap_la_LIBADD = @LTLIBOBJS@
--- /dev/null
+# Makefile.in generated by automake 1.10.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 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@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@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@
+subdir = src/win
+DIST_COMMON = README $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in mmap.c
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libwinmmap_la_DEPENDENCIES = @LTLIBOBJS@
+am_libwinmmap_la_OBJECTS = nonempty.lo
+libwinmmap_la_OBJECTS = $(am_libwinmmap_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/config
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libwinmmap_la_SOURCES)
+DIST_SOURCES = $(libwinmmap_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+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@
+MAKEINFO = @MAKEINFO@
+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_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_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+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@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libwinmmap.la
+noinst_HEADERS = mman.h
+libwinmmap_la_SOURCES = nonempty.c
+libwinmmap_la_LIBADD = @LTLIBOBJS@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(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/win/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/win/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
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libwinmmap.la: $(libwinmmap_la_OBJECTS) $(libwinmmap_la_DEPENDENCIES)
+ $(LINK) $(libwinmmap_la_OBJECTS) $(libwinmmap_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mmap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nonempty.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+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 $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$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
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+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:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf $(DEPDIR) ./$(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:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags 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-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 tags uninstall uninstall-am
+
+# 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:
--- /dev/null
+This directory contains functions that are missing on the Windows platform. In
+particular, mmap, munmap, and msync are not available on Windows. These
+replacements are used on both Cygwin and MinGW. (On Cygwin the built-in mmap
+has no write support, and is not used).
+
+mmap.c
+mman.h
+
+Special thanks to Horea Haitonic for contributing mmap.c and mman.h.
+
+April 2007
--- /dev/null
+#ifndef _MMAN_H_
+#define _MMAN_H_
+
+/* Protections */
+#define PROT_NONE 0x00 /* no permissions */
+#define PROT_READ 0x01 /* pages can be read */
+#define PROT_WRITE 0x02 /* pages can be written */
+#define PROT_EXEC 0x04 /* pages can be executed */
+
+/* Sharing type and options */
+#define MAP_SHARED 0x0001 /* share changes */
+#define MAP_PRIVATE 0x0002 /* changes are private */
+#define MAP_COPY MAP_PRIVATE /* Obsolete */
+#define MAP_FIXED 0x0010 /* map addr must be exactly as requested */
+#define MAP_RENAME 0x0020 /* Sun: rename private pages to file */
+#define MAP_NORESERVE 0x0040 /* Sun: don't reserve needed swap area */
+#define MAP_INHERIT 0x0080 /* region is retained after exec */
+#define MAP_NOEXTEND 0x0100 /* for MAP_FILE, don't change file size */
+#define MAP_HASSEMAPHORE 0x0200 /* region may contain semaphores */
+#define MAP_STACK 0x0400 /* region grows down, like a stack */
+
+/* Error returned from mmap() */
+#define MAP_FAILED ((void *)-1)
+
+/* Flags to msync */
+#define MS_ASYNC 0x01 /* perform asynchronous writes */
+#define MS_SYNC 0x02 /* perform synchronous writes */
+#define MS_INVALIDATE 0x04 /* invalidate cached data */
+
+/* File modes for 'open' not defined in MinGW32 (not used by mmap) */
+#ifndef S_IWGRP
+#define S_IWGRP 0
+#define S_IRGRP 0
+#define S_IROTH 0
+#endif
+
+/**
+ * Map a file to a memory region
+ */
+void *mmap(void *addr, unsigned int len, int prot, int flags, int fd, unsigned int offset);
+
+/**
+ * Unmap a memory region
+ */
+int munmap(void *addr, int len);
+
+/**
+ * Synchronize a mapped region
+ */
+int msync(char *addr, int len, int flags);
+
+#endif /* _MMAN_H_ */
--- /dev/null
+#include <stdlib.h>
+#include <windows.h>
+#ifdef _WIN32
+#include <io.h>
+#endif
+#include <errno.h>
+#include "mman.h"
+
+static const char id[]="$Id: tpl.c 107 2007-04-20 17:11:29Z thanson $";
+
+/**
+ * @brief Map a file to a memory region
+ *
+ * This function emulates the POSIX mmap() using CreateFileMapping() and
+ * MapViewOfFile()
+ *
+ * @param addr the suggested start address (if != 0)
+ * @param len length of the region
+ * @param prot region accesibility, bitwise OR of PROT_READ, PROT_WRITE, PROT_EXEC
+ * @param flags mapping type and options (ignored)
+ * @param fd object to be mapped into memory
+ * @param offset offset into mapped object
+ * @return pointer to the memory region, or NULL in case of error
+ */
+void *mmap(void *addr, unsigned int len, int prot, int flags, int fd, unsigned int offset)
+{
+ DWORD wprot;
+ DWORD waccess;
+ HANDLE h;
+ void *region;
+
+ /* Translate read/write/exec flags into WIN32 constants */
+ switch (prot) {
+ case PROT_READ:
+ wprot = PAGE_READONLY;
+ break;
+ case PROT_EXEC:
+ wprot = PAGE_EXECUTE_READ;
+ break;
+ case PROT_READ | PROT_EXEC:
+ wprot = PAGE_EXECUTE_READ;
+ break;
+ case PROT_WRITE:
+ wprot = PAGE_READWRITE;
+ break;
+ case PROT_READ | PROT_WRITE:
+ wprot = PAGE_READWRITE;
+ break;
+ case PROT_READ | PROT_WRITE | PROT_EXEC:
+ wprot = PAGE_EXECUTE_READWRITE;
+ break;
+ case PROT_WRITE | PROT_EXEC:
+ wprot = PAGE_EXECUTE_READWRITE;
+ break;
+ }
+
+ /* Obtaing handle to map region */
+ h = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, wprot, 0, len, 0);
+ if (h == NULL) {
+ DWORD error = GetLastError();
+
+ /* Try and translate some error codes */
+ switch (error) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_INVALID_ACCESS:
+ errno = EACCES;
+ break;
+ case ERROR_OUTOFMEMORY:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ errno = ENOMEM;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ return MAP_FAILED;
+ }
+
+
+ /* Translate sharing options into WIN32 constants */
+ switch (wprot) {
+ case PAGE_READONLY:
+ waccess = FILE_MAP_READ;
+ break;
+ case PAGE_READWRITE:
+ waccess = FILE_MAP_WRITE;
+ break;
+ }
+
+ /* Map file and return pointer */
+ region = MapViewOfFile(h, waccess, 0, 0, 0);
+ if (region == NULL) {
+ DWORD error = GetLastError();
+
+ /* Try and translate some error codes */
+ switch (error) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_INVALID_ACCESS:
+ errno = EACCES;
+ break;
+ case ERROR_INVALID_HANDLE:
+ errno = EBADF;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ CloseHandle(h);
+ return MAP_FAILED;
+ }
+
+ /* All fine */
+ return region;
+}
+
+
+/**
+ * @brief Unmap a memory region
+ *
+ * This is a wrapper around UnmapViewOfFile in the win32 API
+ *
+ * @param addr start address
+ * @param len length of the region
+ * @return 0 for success, -1 for error
+ */
+int munmap(void *addr, int len)
+{
+ if (UnmapViewOfFile(addr)) {
+ return 0;
+ }
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+
+/**
+ * Synchronize a mapped region
+ *
+ * This is a wrapper around FlushViewOfFile
+ *
+ * @param addr start address
+ * @param len number of bytes to flush
+ * @param flags sync options -- currently ignored
+ * @return 0 for success, -1 for error
+ */
+int msync(char *addr, int len, int flags)
+{
+ if (FlushViewOfFile(addr, len) == 0) {
+ DWORD error = GetLastError();
+
+ /* Try and translate some error codes */
+ switch (error) {
+ case ERROR_INVALID_PARAMETER:
+ errno = EINVAL;
+ break;
+ case ERROR_WRITE_FAULT:
+ errno = EIO;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ return -1;
+ }
+
+ /* Success */
+ return 0;
+}
--- /dev/null
+/* This function exists solely to prevent libwinmmap.la from being empty. Empty
+ * libraries cause problems on some platforms, e.g. Mac OS X. */
+
+int tpl_nonempty(int i) {
+ return i+1;
+}
--- /dev/null
+# Makefile for tpl built-in test suite
+#
+# This Makefile has three useful targets:
+#
+# all (default):
+# Build and run all self-tests by compiling tpl into each test.
+#
+# Note: On Cygwin/MinGW, compiling the tpl source directly into the tests is
+# not supported (as they use 'replacement' functions which get included only
+# in libtpl), so on these platforms the 'alt' target is the default.
+#
+# alt:
+# Build and run all the self-tests by linking them with libtpl, which must
+# have been created beforehand (by running 'configure; make' in the
+# top-level directory); a reminder will be printed if you have not done so.
+#
+# Note, libtool will create wrappers around each test to accomodate the
+# pre-installed state of ../src/libtpl.la. In a real program, you'd link
+# against an installed libtpl.la, and these wrappers would not be used.
+#
+# clean:
+# Clean up all the compiled bits.
+#
+PROGS = test1 test2 test3 test4 test5 test6 test7 test8 \
+ test9 test10 test11 test12 test13 test14 test15 test16 \
+ test17 test18 test19 test20 test21 test22 test23 test24 \
+ test25 test26 test27 test28 test29 test30 test31 test32 \
+ test33 test34 test35 test36 test37 test38 test39 test40 \
+ test41 test42 test43 test44 test45 test46 test47 test48 \
+ test49 test50 test51 test52 test53 test54 test55 test56 \
+ test57 test58 test59 test60 test61 test62 test63 test64 \
+ test65 test66 test67 test68 test69 test70 test71 test72 \
+ test73 test74 test75 test76 test77 test78 test79 test80 \
+ test81 test82 test83 test84 test85 test86 test87 test88 \
+ test89 test90 test91 test92 test93 test94 test95 test96 \
+ test97 test98 test99 test100 test101 test102 test103 test104 \
+ test105 test106 test107 test108 test109 test110 test111 test112 \
+ test113 test114 test115 test116 test117 test118 test119 test120 \
+ test121 test122 test123 test124
+
+TPLSRC = ../src
+CFLAGS = -I$(TPLSRC) -g
+CFLAGS += -pedantic
+CFLAGS += -Wall
+#CFLAGS += -m32
+#CFLAGS += -m64
+CFLAGS += -O3
+#For testing without C99 feature support
+#CFLAGS += -std=c89
+
+# Prefer 64-bit compilation on Mac OS X (not necessary, just faster)
+ifneq ($(strip $(shell $(CC) -v 2>&1 |egrep "i[0-9]+-apple-darwin")),)
+ CFLAGS += -m64
+endif
+
+# detect Cygwin or MinGW
+ifneq ($(strip $(shell $(CC) -v 2>&1 |egrep "cygwin|mingw")),)
+ TESTS=./do_tests.cygwin
+ # divert to the alt target; use tpl as a lib for Cygwin/Mingw
+ TARGET=alt
+else
+ TESTS=./do_tests
+ TARGET=$(PROGS) run_tests
+endif
+
+all: $(TARGET)
+
+tpl.o : $(TPLSRC)/tpl.c $(TPLSRC)/tpl.h
+ $(CC) -c $(CFLAGS) $(TPLSRC)/tpl.c
+
+$(PROGS) : tpl.o
+ $(CC) $(CFLAGS) -o $@ $(@).c tpl.o
+
+run_tests:
+ perl $(TESTS)
+
+# This target can be used to compile the tests as dependent on
+# the tpl library (rather than compiling in the tpl source).
+alt: mkalttests run_tests
+
+mkalttests:
+ @$(MAKE) -f Makefile.alt PROGS="$(PROGS)"
+
+.PHONY: clean
+
+clean:
+ rm -f $(PROGS) tpl.o test*.out test*.err test*.exe
+ rm -rf $(PROGS) test*.dSYM
--- /dev/null
+# This Makefile.alt is subordinate to Makefile (usage: make alt)
+# Its distinction is that it builds the test programs by linking
+# them with libtpl rather than by compiling tpl.c into the tests.
+SRC = ../src
+LIBTOOL = ../libtool
+LTLIB = $(SRC)/libtpl.la
+CFLAGS = -I$(SRC) -g
+
+# We have an alternate basic test for MinGW
+ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "mingw")),)
+ TARGET=mingw
+else
+ TARGET=$(PROGS)
+endif
+
+all: $(TARGET)
+
+
+$(PROGS) : $(LTLIB)
+ $(CC) -c $(CFLAGS) $(@).c
+ $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $(@).o $(LTLIB)
+
+$(LTLIB) :
+ @echo "you must first run 'configure; make' in the top-level directory"
+ @exit 1
+
+mingw :
+ @echo ""
+ @echo "MinGW has its own test suite. Please run make -f Makefile.mingw"
+ @echo ""
+ @exit 1
--- /dev/null
+# This makes a small Mingw test program. The standard test suite
+# is incompatible with MinGW because it uses UNIX facilities such
+# as pipe/fork and the /tmp directory, not features of Windows.
+SRC = ../src
+LIBTOOL = ../libtool
+LTLIB = $(SRC)/libtpl.la
+CFLAGS = -I$(SRC) -g
+
+PROG = mgwtest
+
+all: $(PROG)
+
+$(PROG) : $(LTLIB)
+ $(CC) -c $(CFLAGS) $(@).c
+ $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $(@).o $(LTLIB) -mwindows
+ @echo "you can now run windows application: $@"
+
+$(LTLIB) :
+ @echo "you must first run 'configure; make' in the top-level directory"
+ @exit 1
+
+.PHONY: clean
+
+clean:
+ rm $(PROG) $(PROG).exe $(PROG).o $(PROG).tpl
--- /dev/null
+Run "make" in this directory to build the tests and run them.
+
+test1: serialize int into memory, unserialize
+test2: serialize int into file, unserialize
+test3: serialize A(i) into file, unserialize
+test4: serialize A(i) into memory, unserialize
+test5: serialize A(A(i)) into memory, unserialize
+test6: serialize string into memory, unserialize
+test7: serialize A(s) into file, unserialize
+test8: serialize cA(c) into file, unserialize
+test9: unpack big-endian tpl data file of A(i)
+test10: unpack little-endian tpl data file of A(i)
+test11: try to load a corrupt tpl image w/invalid chars
+test12: try to load a corrupt tpl image w/runaway format string
+test13: try to load a corrupt tpl image w/internal A length -1
+test14: try to load a corrupt tpl image w/internal A length +1
+test15: try to load a corrupt tpl image w/invalid magic
+test16: try to load a corrupt tpl image w/invalid len
+test17: try to load a good tpl but whose format mismatches map
+test18: try to map a tpl with malformed format- unbalanced parens: missing )
+test19: try to map a tpl with malformed format- unbalanced parens: extra )
+test20: try to map a tpl with malformed format- empty A()
+test21: serialize A(ii) into file, unserialize
+test22: serialize A(u) into file, unserialize
+test23: serialize A(u) into file, read tpl from fd using TPL_FD
+test24: read A(u) tpl file with extra trailing bytes, ok in TPL_FD mode
+test25: same A(u) tpl file with extra trailing bytes, not ok in TPL_FILE mode
+test26: test non-blocking tpl_gather using async read of 3 tpls across 2 pipes
+test27: test tpl_dump() of A(u) to file using TPL_FD, unserialize
+test28: parent writes A(u) tpl to child through pipe, both use TPL_FD mode
+test29: parent writes consecutive A(u) tpl then A(c) tpl to child through pipe
+test30: test pack B (binary buffer) and unpack
+test31: test pack B (binary buffer) of 0-length and unpack
+test32: test pack A(B) and unpack
+test33: test pack f (double) and unpack
+test34: test pack A(f) and unpack
+test35: pack A(is)
+test36: unpack A(is)
+test37: pack A(A(i)) [example from man page]
+test38: unpack A(A(i)) [example from man page]
+test39: try to load a tpl with an unsupported bit flag set
+test40: pack char array - userguide example
+test41: unpack char arrray - userguide example
+test42: test non-aligned pointers in backbone (under Solaris dbx, check -all)
+test43: test non-aligned pointers in backbone (under Solaris dbx, check -all)
+test44: test non-aligned pointers in backbone (under Solaris dbx, check -all)
+test46: test correct-size of backbone "double" datum (Solaris dbx, check -all)
+test47: store A(i) to file - userguide example
+test48: read A(i) from file - userguide example
+test49: write A(s) - userguide example
+test50: read A(s) - userguide example
+test51: test tpl_mem_gather (_0: 1 tpl; _1: 2 tpls; _2/_3/_4: 1 tpl in 3 parts)
+test52: A(A(i)): pack an int; pack parent; pack int; don't pack parent; ser_osz
+test53: A(A(i)): pack an int; pack parent; pack parent; 0-length 2nd parent el.
+test54: test callback negative return value for tpl_mem_gather
+test55: test callback negative return value for tpl_fd_gather
+test56: test static string using c# format pack/unpack in mem
+test57: test pack static string using c# to file
+test58: test unpack static string using c# from file
+test59: test alignment using cc#cc# pack/unpack in mem
+test60: test pack-then-load (implicit intervening free, using tpl_free_keep_map)
+test61: test load-then-load (implicit intervening free, using tpl_free_keep_map)
+test62: test load-then-pack (implicit intervening free, using tpl_free_keep_map)
+test63: test pack-then-unpack (implicit dump/load) then pack-then-unpack again
+test64: pack level 0 types, change and re-pack level 0 types, test implicit free
+test65: pack int[] using format character #
+test66: pack two separate int[] using format character #
+test67: test expected failure if format strings agree but array lengths mismatch
+test68: test octothorpe support by packing,unpacking two fixed lengths arrays
+test69: test octothorpic array support A(i#i#)
+test70: test S(...) structure pack and unpack
+test71: test cS(...) pack/unpack when preceded by non-structure byte
+test72: test wildcard structure unpack
+test73: test wildcard structure unpack
+test74: test wildcard structure unpack
+test75: test sc# (string and byte array)
+test76: test S(sc#) (structure of last)
+test77: test S(sc#) (structure of last) with wildcard unpack
+test78: pack A(i)c
+test79: unpack A(i)c
+test80: pack and unpack A(S(ci#))
+test81: pack and unpack A(S(ci#))
+test82: pack cA(i#)S(cf#)A(ci#)
+test83: unpack cA(i#)S(cf#)A(ci#)
+test84: repeat test83 with both big and little endian input files
+test85: tpl_peek at file
+test86: tpl_peek at in-memory tpl
+test87: test tpl_gather(TPL_GATHER_FD_BLOCKING)
+test88: test packing S(ic#f)
+test89: test unpacking S(ic#f) as S(*)
+test90: pack and unpack I
+test91: pack and unpack U
+test92: pack and unpack A(cIcU)
+test93: pack and unpack NULL string
+test94: pack and unpack A(s) with some null
+test95: pack and unpack null string, empty string, non-empty string
+test96: pack and unpack A(null string, empty string, non-empty string)
+test97: pack and unapck 16-bit int/uint (j,v)
+test98: pack and unapck 16-bit int/uint A(j,v)
+test99: data peek at c in complex format
+test100: data peek at i inside S(ic)
+test101: data peek at c inside S(ic) [expected failure test]
+test102: data peek at c in simple format c
+test103: data peek at iscsi in S(iscsiu)
+test104: data peek at iscsi in S(iscsiu) with NULL string pointer
+test105: tpl_jot then unpack by normal then by tpl_peek
+test106: test IS(Iiuijc#)#iiii
+test107: test S(ic#)#
+test108: test IS(Iiuijc#)#iiii
+test109: test S(cijc)# where next structure elt alignment based on i
+test110: test ssssiiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiii
+test111: test S(icfv#)#
+test112: test S(ic#fv#)#
+test113: test icS(ic#fv#)#ci
+test114: test multi-dimension i##
+test115: test S(s)#, S(si)#, S(c#si)#
+test116: test cs#i
+test117: test cA(s#)i
+test118: test tpl_peek(TPL_FXLENS) with cA(i#)S(cf#)A(ci#)
+test119: test tpl_dump(tn,TPL_GETSIZE,&sz);
+test120: test TPL_PREALLOCD and TPL_EXCESS_OK flags
+test121: test s##
+test122: test S(ic#f$(ci))
+test123: setjmp/longjmp based fatal error handler
+test124: test A(S(c#)s) as per bug report from Eric Rose
--- /dev/null
+#!/bin/sh
+
+# This script works with Solaris Studio 11
+# version of dbx which supports "check -all" and
+# "check -access" run modes, at least on Sparc.
+# These detect access or alignment violations or
+# leftover unfreed memory. TDH 29Dec06
+
+DBX=/opt/SUNWspro/bin/dbx
+OUT=/tmp/dbx.out.$$
+
+echo "Writing $OUT..."
+
+for f in test? test??
+do
+ echo $f
+ ${DBX} $f 1>>${OUT} 2>&1 <<EOF
+check -all
+run
+EOF
+
+egrep 'mar|rua|rui|wui|wua|maw' ${OUT}
+
+done
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my @tests;
+for (glob "test*[0-9]") {
+ push @tests, $_ if -e "$_.ans";
+}
+
+my $num_failed=0;
+
+for my $test (@tests) {
+ `./$test > $test.out 2> $test.err`;
+ `diff $test.out $test.ans`;
+ print "$test failed\n" if $?;
+ $num_failed++ if $?;
+ unlink "$test.err" if -z "$test.err";
+}
+
+print scalar @tests . " tests conducted, $num_failed failed.\n";
+exit $num_failed;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my @tests;
+for (glob "test*[0-9].exe") {
+ push @tests, "$_" if -e substr($_, 0, - 4).".ans";
+}
+
+my $num_failed=0;
+
+for my $test (@tests) {
+ `./$test > $test.out`;
+ my $ansfile = substr($test, 0, - 4).".ans";
+ `diff $test.out $ansfile`;
+ print "$test failed\n" if $?;
+ $num_failed++ if $?;
+}
+
+print scalar @tests . " tests conducted, $num_failed failed.\n";
--- /dev/null
+#include <stdio.h>
+#include <inttypes.h>
+
+/* try compiling this with -m32 vs -m64
+ *
+ * with mac os x and gcc,
+ * on -m32 the int64_t gets aligned at +4
+ * on -m64 the int64_t gets aligned at +8
+ */
+
+static const struct s_t {
+ int i;
+ int64_t j;
+} s;
+
+int main() {
+ if ((long)&s.j % 8 != 0) printf("non-aligned int64\n");
+ else printf("aligned int64\n");
+}
--- /dev/null
+#include <stdio.h>
+
+/* try compiling this with and without aligned doubles
+ *
+ * cc -malign-double -o malign malign.c
+ * cc -mno-align-double -o malign malign.c
+ *
+ * on x86, double is not normally aligned (unless -malign-double is used).
+ * but on Sparc, or x86-64, double is aligned.
+ */
+
+static const struct s_t {
+ char a;
+ double d;
+} s;
+
+int main() {
+ if ((long)&s.d % 8 != 0) printf("-mno-align-double\n");
+ else printf("-malign-double\n");
+}
--- /dev/null
+#include <windows.h>
+#include "tpl.h"
+
+int WINAPI WinMain (HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ PSTR szCmdLine,
+ int iCmdShow)
+{
+ char *status;
+ int rc=0,i,j;
+ tpl_node *tn;
+ void *img;
+ size_t sz;
+
+ tn = tpl_map("A(i)", &i);
+ for(i=0; i<10; i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_MEM,&img, &sz);
+ tpl_free(tn);
+
+ j=0;
+ tn = tpl_map("A(i)", &i);
+ tpl_load(tn,TPL_MEM,img,sz);
+ while(tpl_unpack(tn,1) > 0) {
+ if (i != j++) {
+ rc = -1;
+ break;
+ }
+ }
+ tpl_free(tn);
+
+ MessageBox (NULL, (rc==0)?"Test1 passed":"Test1 failed",
+ "MinGW Tpl Test", MB_OK);
+
+ /* Test 2 */
+ tn = tpl_map("A(i)", &i);
+ for(i=0; i<10; i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"mgwtest.tpl");
+ tpl_free(tn);
+
+ j=0;
+ tn = tpl_map("A(i)", &i);
+ tpl_load(tn,TPL_FILE,"mgwtest.tpl");
+ while(tpl_unpack(tn,1) > 0) {
+ if (i != j++) {
+ rc = -1;
+ break;
+ }
+ }
+ tpl_free(tn);
+
+ MessageBox (NULL, (rc==0)?"Test2 passed":"Test2 failed",
+ "MinGW Tpl Test", MB_OK);
+
+ return (0);
+}
+
--- /dev/null
+SRCDIR=../../src
+CPPFLAGS = -I$(SRCDIR)
+
+PROGS = other1
+all: $(PROGS) run_tests
+
+tpl.c:
+ cp $(SRCDIR)/tpl.c .
+
+other1: other1.o tpl.o
+ g++ -o other1 other1.o tpl.o
+
+
+.PHONY: clean run_tests
+
+clean:
+ rm -f *.o $(PROGS) *.out
+ rm -f tpl.c
+
+run_tests:
+ perl ./do_tests
--- /dev/null
+Assorted other tests that are not part of the standard test suite.
+other1: a C++ program using "this" as a structure pointer
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my @tests;
+for (glob "other*[0-9]") {
+ push @tests, $_ if -e "$_.ans";
+}
+
+my $num_failed=0;
+
+for my $test (@tests) {
+ `./$test > $test.out 2> $test.err`;
+ `diff $test.out $test.ans`;
+ print "$test failed\n" if $?;
+ $num_failed++ if $?;
+ unlink "$test.err" if -z "$test.err";
+}
+
+print scalar @tests . " tests conducted, $num_failed failed.\n";
+exit $num_failed;
--- /dev/null
+buffer: tpl
+i: 9999
+c: this is a test string.
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "tpl.h"
+main() {
+void *buffer;
+size_t bsize;
+
+struct ci {
+ int i;
+ char c[30];
+
+ void pack( void **buffer, size_t *size )
+ {
+ tpl_node *tn = tpl_map("S(ic#)", this, 30); /* pass structure address */
+ tpl_pack(tn, 0);
+ tpl_dump(tn, TPL_MEM, buffer, size);
+ tpl_free(tn);
+ }
+
+ void unpack( void *buffer, size_t size )
+ {
+ tpl_node *tn = tpl_map("S(ic#)", this, 30);
+ tpl_load(tn, TPL_MEM, buffer, size);
+ tpl_unpack( tn, 0 );
+ tpl_free(tn);
+ }
+};
+
+struct ci s = {9999, "this is a test string."};
+
+s.pack(&buffer, &bsize);
+printf("buffer: %s\n", (char *)buffer);
+
+struct ci b = { -1, "" };
+
+b.unpack(buffer, bsize);
+
+printf("i: %d\n", b.i);
+printf("c: %s\n", b.c);
+}
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Config;
+
+#print "$_: ", ($Config{$_} or ""), "\n" for keys %Config;
+#exit;
+
+my @types = qw(char short int long longlong double);
+for (@types) {
+ my $bytes = $Config{"${_}size"};
+ my $bits = $bytes * 8;
+ printf "size of %-10s: %5d bytes (%d bits)\n", $_, $bytes, $bits;;
+}
+
+print qq/
+ These sizes reflect the platform and compiler options with
+ which Perl was built on this system. The sizes may change
+ if different compiler options are used.
+/;
+
--- /dev/null
+#include <windows.h>
+#include "tpl.h"
+
+int WINAPI WinMain (HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ PSTR szCmdLine,
+ int iCmdShow)
+{
+ MessageBox (NULL, "Hello", "Hello Demo", MB_OK);
+ return (0);
+}
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i,j=-1;
+ void *addr;
+ size_t sz;
+
+ tn = tpl_map("i",&i);
+ i=1;
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_MEM,&addr,&sz);
+ tpl_free(tn);
+
+ tn = tpl_map("i",&j);
+ tpl_load(tn,TPL_MEM,addr,sz);
+ tpl_unpack(tn,0);
+ printf("j is %d\n", j);
+ tpl_free(tn);
+ free(addr);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test10.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+fmt: S(ic)
+p: 1
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename = "/tmp/test100.tpl";
+int main() {
+ tpl_node *tn;
+ struct {
+ int i;
+ char c;
+ } s;
+ int p;
+ char *fmt;
+
+ tn = tpl_map("S(ic)", &s);
+ s.i = 1; s.c = '^';
+ tpl_pack(tn, 0);
+ tpl_dump(tn, TPL_FILE, filename);
+ tpl_free(tn);
+
+ fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "i", &p);
+ if (fmt) {
+ printf("fmt: %s\n", fmt);
+ printf("p: %d\n", p);
+ }
+
+ return 0;
+}
--- /dev/null
+fmt: S(ic)
+p: 1
--- /dev/null
+TPL_DATAPEEK format mismatches tpl iamge
+peek failed
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+extern tpl_hook_t tpl_hook;
+const char *filename = "/tmp/test101.tpl";
+int main() {
+ tpl_node *tn;
+ struct {
+ int i;
+ char c;
+ } s;
+ char *fmt, q;
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("S(ic)", &s);
+ s.i = 1; s.c = '^';
+ tpl_pack(tn, 0);
+ tpl_dump(tn, TPL_FILE, filename);
+ tpl_free(tn);
+
+ fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "c", &q);
+ if (fmt) {
+ printf("fmt: %s\n", fmt);
+ printf("q: %c\n", q);
+ } else {
+ printf("peek failed\n");
+ }
+
+ return 0;
+}
--- /dev/null
+TPL_DATAPEEK format mismatches tpl iamge
+peek failed
--- /dev/null
+fmt: c
+q: !
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename = "/tmp/test102.tpl";
+int main() {
+ tpl_node *tn;
+ char *fmt,p,q;
+
+ tn = tpl_map("c", &p);
+ p = '!';
+ tpl_pack(tn, 0);
+ tpl_dump(tn, TPL_FILE, filename);
+ tpl_free(tn);
+
+ fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "c", &q);
+ if (fmt) {
+ printf("fmt: %s\n", fmt);
+ printf("q: %c\n", q);
+ }
+
+ return 0;
+}
--- /dev/null
+fmt: c
+q: !
--- /dev/null
+fmt: S(iscsiu)
+pi: 1, ps: hello, pc: ^, pt: world, pi: 2
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename = "/tmp/test103.tpl";
+int main() {
+ tpl_node *tn;
+ struct {
+ int i;
+ char *s;
+ char c;
+ char *t;
+ int j;
+ unsigned u;
+ } s;
+ char *fmt, *ps, pc, *pt;
+ int pi, pj;
+
+ tn = tpl_map("S(iscsiu)", &s);
+ s.i = 1; s.s = "hello"; s.c = '^'; s.t = "world"; s.j = 2; s.u = 3;
+ tpl_pack(tn, 0);
+ tpl_dump(tn, TPL_FILE, filename);
+ tpl_free(tn);
+
+ fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "iscsi",&pi,&ps,&pc,&pt,&pj);
+ if (fmt) {
+ printf("fmt: %s\n", fmt);
+ printf("pi: %d, ps: %s, pc: %c, pt: %s, pi: %d\n", pi,ps,pc,pt,pj);
+ } else {
+ printf("peek failed\n");
+ }
+
+ return 0;
+}
--- /dev/null
+fmt: S(iscsiu)
+pi: 1, ps: hello, pc: ^, pt: world, pi: 2
--- /dev/null
+fmt: S(iscsiu)
+pi: 1, ps: NULL, pc: ^, pt: world, pi: 2
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename = "/tmp/test104.tpl";
+int main() {
+ tpl_node *tn;
+ struct {
+ int i;
+ char *s;
+ char c;
+ char *t;
+ int j;
+ unsigned u;
+ } s;
+ char *fmt, *ps, pc, *pt;
+ int pi, pj;
+
+ tn = tpl_map("S(iscsiu)", &s);
+ s.i = 1; s.s = NULL; s.c = '^'; s.t = "world"; s.j = 2; s.u = 3;
+ tpl_pack(tn, 0);
+ tpl_dump(tn, TPL_FILE, filename);
+ tpl_free(tn);
+
+ fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "iscsi",&pi,&ps,&pc,&pt,&pj);
+ if (fmt) {
+ printf("fmt: %s\n", fmt);
+ printf("pi: %d, ps: %s, pc: %c, pt: %s, pi: %d\n",pi,ps?ps:"NULL",pc,pt,pj);
+ } else {
+ printf("peek failed\n");
+ }
+
+ return 0;
+}
--- /dev/null
+fmt: S(iscsiu)
+pi: 1, ps: NULL, pc: ^, pt: world, pi: 2
--- /dev/null
+i: 1, s: hello, w: world, c: $
+i: 1, s: hello, w: world, c: $
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename = "/tmp/test105.tpl";
+
+int main() {
+ int i=1;
+ char *s="hello",*w="world",c='$', *fmt;
+ tpl_node *tn;
+
+ tpl_jot(TPL_FILE, filename, "issc", &i, &s, &w, &c);
+
+ i = 0; s = NULL; w = NULL; c = 0;
+
+ /* unpack the normal way */
+ tn = tpl_map("issc", &i, &s, &w, &c);
+ tpl_load(tn, TPL_FILE, filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ printf("i: %d, s: %s, w: %s, c: %c\n", i, s, w, c);
+
+ i = 0; s = NULL; w = NULL; c = 0;
+
+ /* unpack the quick way */
+ fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "issc", &i, &s, &w, &c);
+ printf("i: %d, s: %s, w: %s, c: %c\n", i, s, w, c);
+
+ return 0;
+}
--- /dev/null
+i: 1, s: hello, w: world, c: $
+i: 1, s: hello, w: world, c: $
--- /dev/null
+testing with TPL_FILE:
+1000
+ 0, 0, 0, 5000, 15, Deepak
+ 1, 5, 10, 6000, 18, Deepak
+ 2, 10, 20, 7000, 21, Deepak
+ 3, 15, 30, 8000, 24, Deepak
+ 4, 20, 40, 9000, 27, Deepak
+ 5, 25, 50, 10000, 30, Deepak
+ 6, 30, 60, 11000, 33, Deepak
+ 7, 35, 70, 12000, 36, Deepak
+ 8, 40, 80, 13000, 39, Deepak
+9,23,43,16
+testing with TPL_FD:
+1000
+ 0, 0, 0, 5000, 15, Deepak
+ 1, 5, 10, 6000, 18, Deepak
+ 2, 10, 20, 7000, 21, Deepak
+ 3, 15, 30, 8000, 24, Deepak
+ 4, 20, 40, 9000, 27, Deepak
+ 5, 25, 50, 10000, 30, Deepak
+ 6, 30, 60, 11000, 33, Deepak
+ 7, 35, 70, 12000, 36, Deepak
+ 8, 40, 80, 13000, 39, Deepak
+9,23,43,16
--- /dev/null
+#include "tpl.h"
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define SUM_LENGTH 16
+#define MS_COUNT 9
+struct sum_buf {
+ int64_t offset;
+ int len;
+ uint32_t sum1;
+ int chain;
+ uint16_t flags;
+ char sum2[SUM_LENGTH];
+};
+
+struct sum_struct {
+ int64_t flength;
+ struct sum_buf *sums;
+ int count;
+ int blength;
+ int remainder;
+ int s2length;
+};
+
+const char *filename = "/tmp/test106.tpl";
+
+int pack(int use_fd)
+{
+ tpl_node *tn;
+ struct sum_struct ms;
+ int fd=-1,j;
+ unsigned perms;
+
+ perms = S_IRUSR|S_IWUSR;
+ if (use_fd) {
+ if ( (fd=open( filename,O_WRONLY|O_CREAT,perms)) == -1) {
+ printf("failed to open %s: %s", filename, strerror(errno));
+ return(-1);
+ }
+ }
+
+ ms.flength = 1000;
+ ms.count = MS_COUNT;
+ ms.blength = 23;
+ ms.remainder = 43;
+ ms.s2length = 16;
+
+ ms.sums = (struct sum_buf*) malloc((sizeof(struct sum_buf))*ms.count);
+
+ for(j=0;j<ms.count;j++)
+ {
+ ms.sums[j].offset = (uint64_t) j;
+ ms.sums[j].len = j*5;
+ ms.sums[j].sum1 = j*10;
+ ms.sums[j].chain = j*1000+5000;
+ ms.sums[j].flags = j*3 + 15;
+ memset(ms.sums[j].sum2,0,SUM_LENGTH);
+ strcpy(ms.sums[j].sum2,"Deepak");
+ }
+
+ tn = tpl_map( "IS(Iiuijc#)#iiii", &ms.flength,ms.sums,SUM_LENGTH,
+ ms.count,&ms.count,&ms.blength,&ms.remainder,&ms.s2length);
+ tpl_pack( tn, 0 );
+
+ if (use_fd) {
+ tpl_dump(tn,TPL_FD, fd);
+ close(fd);
+ } else {
+ tpl_dump(tn,TPL_FILE,filename);
+ }
+
+ tpl_free( tn );
+
+ return 0;
+}
+
+int unpack(int use_fd) {
+ tpl_node *tn;
+ struct sum_struct ms;
+ unsigned perms;
+ int fd=-1,i;
+
+ perms = S_IRUSR|S_IWUSR;
+ if (use_fd) {
+ if ( (fd=open( filename,O_RDONLY,perms)) == -1) {
+ printf("failed to open %s: %s", filename, strerror(errno));
+ return(-1);
+ }
+ }
+
+ ms.sums = (struct sum_buf*) malloc((sizeof(struct sum_buf))*MS_COUNT);
+
+ tn = tpl_map( "IS(Iiuijc#)#iiii", &ms.flength,ms.sums,SUM_LENGTH,
+ MS_COUNT,&ms.count,&ms.blength,&ms.remainder,&ms.s2length);
+ if (use_fd) tpl_load(tn, TPL_FD, fd);
+ else tpl_load(tn, TPL_FILE, filename);
+ tpl_unpack(tn, 0 );
+ tpl_free( tn );
+ if (use_fd) close(fd);
+
+ printf("%d\n", (int)(ms.flength));
+ for(i=0; i < MS_COUNT; i++) {
+ printf(" %d, %d, %u, %d, %d, %s\n", (int)(ms.sums[i].offset), ms.sums[i].len, ms.sums[i].sum1, ms.sums[i].chain, (int)(ms.sums[i].flags), ms.sums[i].sum2);
+ }
+ printf("%d,%d,%d,%d\n", ms.count, ms.blength, ms.remainder, ms.s2length);
+
+ return 0;
+}
+
+int main() {
+ printf("testing with TPL_FILE:\n");
+ pack(0);
+ unpack(0);
+
+ printf("testing with TPL_FD:\n");
+ pack(1);
+ unpack(1);
+
+ return 0;
+}
--- /dev/null
+testing with TPL_FILE:
+1000
+ 0, 0, 0, 5000, 15, Deepak
+ 1, 5, 10, 6000, 18, Deepak
+ 2, 10, 20, 7000, 21, Deepak
+ 3, 15, 30, 8000, 24, Deepak
+ 4, 20, 40, 9000, 27, Deepak
+ 5, 25, 50, 10000, 30, Deepak
+ 6, 30, 60, 11000, 33, Deepak
+ 7, 35, 70, 12000, 36, Deepak
+ 8, 40, 80, 13000, 39, Deepak
+9,23,43,16
+testing with TPL_FD:
+1000
+ 0, 0, 0, 5000, 15, Deepak
+ 1, 5, 10, 6000, 18, Deepak
+ 2, 10, 20, 7000, 21, Deepak
+ 3, 15, 30, 8000, 24, Deepak
+ 4, 20, 40, 9000, 27, Deepak
+ 5, 25, 50, 10000, 30, Deepak
+ 6, 30, 60, 11000, 33, Deepak
+ 7, 35, 70, 12000, 36, Deepak
+ 8, 40, 80, 13000, 39, Deepak
+9,23,43,16
--- /dev/null
+0 cat
+1 dog
+2 eel
+3 emu
+4 ant
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <string.h>
+
+typedef struct {
+ int i;
+ char c[4];
+} test_t;
+
+const char *filename = "/tmp/test107.tpl";
+
+int main() {
+ test_t s[5], t[5];
+ tpl_node *tn;
+ int i;
+
+ s[0].i = 0; strcpy(s[0].c, "cat");
+ s[1].i = 1; strcpy(s[1].c, "dog");
+ s[2].i = 2; strcpy(s[2].c, "eel");
+ s[3].i = 3; strcpy(s[3].c, "emu");
+ s[4].i = 4; strcpy(s[4].c, "ant");
+
+ tn = tpl_map("S(ic#)#", &s, 4, 5);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("S(ic#)#", &t, 4, 5);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ for(i=0; i < 5; i++) {
+ printf("%d %s\n", s[i].i, s[i].c);
+ }
+
+ return 0;
+}
--- /dev/null
+0 cat
+1 dog
+2 eel
+3 emu
+4 ant
--- /dev/null
+structure matches original
+other fields match original
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+typedef struct {
+ int64_t j;
+ int l1;
+ unsigned l2;
+ int i;
+ int16_t h;
+ char c[4];
+} test_t;
+
+const char *filename = "/tmp/test108.tpl";
+
+int main() {
+ test_t s[5], t[5];
+ tpl_node *tn;
+ int w=10,x=20,y=30,z=40,W,X,Y,Z;
+ uint64_t b=10,B;
+
+ memset(s, 0, sizeof(s));
+ memset(t, 0, sizeof(t));
+
+ s[0].j=0; s[0].i=0; s[0].l1= 0; s[0].l2=0; s[0].h= 0; strcpy(s[0].c, "cat");
+ s[1].j=100; s[1].i=1; s[1].l1=-1; s[1].l2=10; s[1].h=1000; strcpy(s[1].c, "dog");
+ s[2].j=200; s[2].i=2; s[2].l1=-2; s[2].l2=20; s[2].h=2000; strcpy(s[2].c, "eel");
+ s[3].j=300; s[3].i=3; s[3].l1=-3; s[3].l2=30; s[3].h=3000; strcpy(s[3].c, "emu");
+ s[4].j=400; s[4].i=4; s[4].l1=-4; s[4].l2=40; s[4].h=4000; strcpy(s[4].c, "ant");
+
+ tn = tpl_map("IS(Iiuijc#)#iiii", &b, s, 4, 5, &w, &x, &y, &z);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("IS(Iiuijc#)#iiii", &B, t, 4, 5, &W, &X, &Y, &Z);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ if (memcmp(t,s,sizeof(t)) == 0) printf("structure matches original\n");
+ else printf("structure mismatches original\n");
+
+ if (b==B && w==W && x==X && y==Y && z==Z) printf("other fields match original\n");
+ else printf("other fields mismatch originals\n");
+
+ return 0;
+}
--- /dev/null
+structure matches original
+other fields match original
--- /dev/null
+sizeof(s): 12
+structures match
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+#include <inttypes.h>
+
+const char *filename = "/tmp/test109.tpl";
+
+typedef struct {
+ char c;
+ uint32_t i;
+ uint16_t j;
+ char d;
+} spad;
+
+int main() {
+ tpl_node *tn;
+ spad s = {'a', 1, 2, 'b'}, t = {'?', 0, 0, '!'};;
+
+ printf("sizeof(s): %d\n", (int)sizeof(s));;
+ tn = tpl_map("S(cijc)", &s);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("S(cijc)", &t);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ if (s.c==t.c && s.i==t.i && s.j==t.j && s.d==t.d)
+ printf("structures match\n");
+ else
+ printf("structures mismatch\n");
+
+ return 0;
+}
--- /dev/null
+sizeof(s): 12
+structures match
--- /dev/null
+test11.tpl: not a valid tpl file
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test11.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test11.tpl: not a valid tpl file
--- /dev/null
+structures match
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "tpl.h"
+#include <inttypes.h>
+
+const char *filename = "/tmp/test110.tpl";
+
+int nstrcmp(const char *a, const char *b) {
+ if (a==NULL || b==NULL) return (a==NULL && b==NULL)?0:1;
+ return strcmp(a,b);
+}
+
+int main() {
+ tpl_node *tn;
+
+ char *s1,*s2,*s3,*s4;
+ int i5,i6,i7,i8,i9,
+ i10,i11,i12,i13,i14,i15,i16,i17,i18,i19,i20,
+ i21,i22,i23,i24,i25,i26,i27,i28,i29,i30,i31,i32,i33;
+ double f34,f35,f36;
+ int i37,i38,i39,i40;
+
+ char *S1,*S2,*S3,*S4;
+ int I5,I6,I7,I8,I9,
+ I10,I11,I12,I13,I14,I15,I16,I17,I18,I19,I20,
+ I21,I22,I23,I24,I25,I26,I27,I28,I29,I30,I31,I32,I33;
+ double F34,F35,F36;
+ int I37,I38,I39,I40;
+
+ s1=NULL;s2=NULL;s3="testing";s4="some_string";
+ i5=5;i6=6;i7=7;i8=8;i9=9;
+ i10=10;i11=11;i12=12;i13=13;i14=14;i15=15;i16=16;i17=17;i18=18;i19=19;i20=20;
+ i21=21;i22=22;i23=23;i24=24;i25=25;i26=26;i27=27;i28=28;i29=29;i30=30;i31=31;
+ i32=32;i33=33;
+ f34=34.0;f35=35.0;f36=36.0;
+ i37=37;i38=38;i39=39;i40=40;
+
+
+ tn = tpl_map("ssssiiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiii",
+ &s1,&s2,&s3,&s4,
+ &i5,&i6,&i7,&i8,&i9,
+ &i10,&i11,&i12,&i13,&i14,&i15,&i16,&i17,&i18,&i19,&i20,
+ &i21,&i22,&i23,&i24,&i25,&i26,&i27,&i28,&i29,&i30,&i31,&i32,&i33,
+ &f34,&f35,&f36,
+ &i37,&i38,&i39,&i40);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("ssssiiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiii",
+ &S1,&S2,&S3,&S4,
+ &I5,&I6,&I7,&I8,&I9,
+ &I10,&I11,&I12,&I13,&I14,&I15,&I16,&I17,&I18,&I19,&I20,
+ &I21,&I22,&I23,&I24,&I25,&I26,&I27,&I28,&I29,&I30,&I31,&I32,&I33,
+ &F34,&F35,&F36,
+ &I37,&I38,&I39,&I40);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ if (
+ !nstrcmp(s1,S1) && !nstrcmp(s2,S2) && !nstrcmp(s3,S3) && !nstrcmp(s4,S4) &&
+ i5==I5 && i6==I6 && i7==I7 && i8==I8 && i9==I9 && i10==I10 && i11==I11 &&
+ i12==I12 && i13==I13 && i14==I14 && i15==I15 && i16==I16 && i17==I17 &&
+ i18==I18 && i19==I19 && i20==I20 && i21==I21 && i22==I22 && i23==I23 &&
+ i24==I24 && i25==I25 && i26==I26 && i27==I27 && i28==I28 && i29==I29 &&
+ i30==I30 && i31==I31 && i32==I32 && i33==I33 && f34==F34 && f35==F35 &&
+ f36==F36 && i37==I37 && i38==I38 && i39==I39 && i40==I40
+ ) {
+ printf("structures match\n");
+ free(S1); free(S2); free(S3); free(S4);
+ }
+ else
+ printf("structures mismatch\n");
+
+ return 0;
+}
--- /dev/null
+structures match
--- /dev/null
+structures match
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include "tpl.h"
+
+const char *filename = "/tmp/test111.tpl";
+
+#define NUM 100
+
+struct st {
+ int i;
+ char c;
+ double f;
+ uint16_t v[2];
+};
+
+
+int main() {
+ struct st s[NUM], d[NUM];
+ tpl_node *tn;
+ int i;
+
+ memset(s, 0, sizeof(s)); /* clear s */
+ memset(d, 0, sizeof(d)); /* clear d */
+
+ /* fill s with random stuff */
+ for(i=0; i < NUM; i++) {
+ s[i].i = i; s[i].c='a'+i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i;
+ }
+
+ tn = tpl_map("S(icfv#)#", s, 2, NUM);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("S(icfv#)#", d, 2, NUM);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ /* see if the result is the same as the s */
+ printf("structures %s\n", (!memcmp(d,s,sizeof(d)))? "match" : "mismatch");
+ return 0;
+}
+
--- /dev/null
+structures match
--- /dev/null
+structures match
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include "tpl.h"
+
+const char *filename = "/tmp/test112.tpl";
+
+#define NUM 10
+
+struct st {
+ int i;
+ char c[8];
+ double f;
+ uint16_t v[2];
+};
+
+
+int main() {
+ struct st s[NUM], d[NUM];
+ tpl_node *tn;
+ int i;
+
+ memset(s, 0, sizeof(s)); /* clear s */
+ memset(d, 0, sizeof(d)); /* clear d */
+
+ /* fill s with random stuff */
+ for(i=0; i < NUM; i++) {
+ s[i].i = i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i;
+ strncpy(s[i].c, "abcdefg",8);
+ s[i].c[0] += 1;
+ }
+
+ tn = tpl_map("S(ic#fv#)#", s, 8, 2, NUM);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("S(ic#fv#)#", d, 8, 2, NUM);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ /* see if the result is the same as the s */
+ printf("structures %s\n", (!memcmp(d,s,sizeof(d)))? "match" : "mismatch");
+ return 0;
+}
+
--- /dev/null
+structures match
--- /dev/null
+structures match
+A matches
+B matches
+C matches
+D matches
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include "tpl.h"
+
+const char *filename = "/tmp/test113.tpl";
+
+#define NUM 10
+
+struct st {
+ int i;
+ char c[8];
+ double f;
+ uint16_t v[2];
+};
+
+
+int main() {
+ struct st s[NUM], t[NUM];
+ tpl_node *tn;
+ int i;
+ int a=5,d=8, A, D;
+ char b='6',c='7', B, C;
+
+ memset(s, 0, sizeof(s)); /* clear s */
+ memset(t, 0, sizeof(t)); /* clear t */
+
+ /* fill s with random stuff */
+ for(i=0; i < NUM; i++) {
+ s[i].i = i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i;
+ strncpy(s[i].c, "abcdefg",8);
+ s[i].c[0] += 1;
+ }
+
+ tn = tpl_map("icS(ic#fv#)#ci", &a, &b, s, 8, 2, NUM, &c, &d);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("icS(ic#fv#)#ci", &A, &B, t, 8, 2, NUM, &C, &D);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ /* see if the result is the same as the s */
+ printf("structures %s\n", (!memcmp(t,s,sizeof(d)))? "match" : "mismatch");
+ printf("A %s\n", (a==A)? "matches" : "mismatches");
+ printf("B %s\n", (b==B)? "matches" : "mismatches");
+ printf("C %s\n", (c==C)? "matches" : "mismatches");
+ printf("D %s\n", (d==D)? "matches" : "mismatches");
+ return 0;
+}
+
--- /dev/null
+structures match
+A matches
+B matches
+C matches
+D matches
--- /dev/null
+matrices match
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "tpl.h"
+
+#define XDIM 10
+#define YDIM 2
+
+const char *filename = "/tmp/test114.tpl";
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int xy[XDIM][YDIM], XY[XDIM][YDIM];
+ int i,j;
+
+ tpl_hook.oops = printf;
+
+ for(i=0; i<XDIM; i++) {
+ for(j=0; j<YDIM; j++) {
+ xy[i][j] = i+j;
+ XY[i][j] = 0;
+ }
+ }
+
+ tn = tpl_map("i##", xy, XDIM, YDIM);
+ if (!tn) {
+ printf("tpl_map failed; exiting\n");
+ return -1;
+ }
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("i##", XY, XDIM, YDIM);
+ if (!tn) {
+ printf("tpl_map failed; exiting\n");
+ return -1;
+ }
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ printf("matrices %s\n", (!memcmp(xy,XY,sizeof(xy))) ? "match" : "mismatch");
+ return 0;
+}
--- /dev/null
+matrices match
--- /dev/null
+hello, world0
+hello, world1
+hello, world2
+hello, world3
+hello, world4
+hello, world5
+hello, world6
+hello, world7
+hello, world8
+hello, world9
+hello, world0, 0
+hello, world1, 1
+hello, world2, 2
+hello, world3, 3
+hello, world4, 4
+hello, world5, 5
+hello, world6, 6
+hello, world7, 7
+hello, world8, 8
+hello, world9, 9
+hello, world0, 0
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world1, 1
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world2, 2
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world3, 3
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world4, 4
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world5, 5
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world6, 6
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world7, 7
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world8, 8
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world9, 9
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tpl.h"
+
+#define COUNT 10
+#define BUF_SIZE 256
+const char *filename = "/tmp/test115.tpl";
+
+typedef struct {
+ char* s;
+} s1_t;
+
+
+typedef struct {
+ char* s;
+ int i;
+} s2_t;
+
+
+typedef struct {
+ char c[BUF_SIZE];
+ char* s;
+ int i;
+} s3_t;
+
+
+const char hw[]="hello, world!";
+
+int main ()
+{
+ tpl_node* tn;
+ s1_t* s1, *S1;
+ s2_t* s2, *S2;
+ s3_t* s3, *S3;
+ int i;
+
+ /* case 1: */
+ s1 = (s1_t*)calloc (sizeof (s1_t), COUNT);
+ for(i=0; i < COUNT; i++) {
+ s1[i].s = malloc(sizeof(hw));
+ memcpy(s1[i].s, hw, sizeof(hw));
+ s1[i].s[sizeof(hw)-2]='0'+i;
+ }
+ tn = tpl_map ("S(s)#", s1, COUNT);
+ tpl_pack (tn, 0);
+ tpl_dump (tn, TPL_FILE, filename);
+ tpl_free (tn);
+ for(i=0; i < COUNT; i++) free(s1[i].s);
+
+ S1 = (s1_t*)calloc (sizeof (s1_t), COUNT);
+ memset(S1, 0xff, sizeof(s1_t)*COUNT);
+ tn = tpl_map ("S(s)#", S1, COUNT);
+ tpl_load (tn, TPL_FILE, filename);
+ tpl_unpack (tn, 0);
+ tpl_free (tn);
+
+ for(i=0; i<COUNT; i++) {
+ printf("%s\n", S1[i].s);
+ }
+
+
+ /* case 2: */
+ s2 = (s2_t*)calloc (sizeof (s2_t), COUNT);
+ for(i=0; i < COUNT; i++) {
+ s2[i].s = malloc(sizeof(hw));
+ memcpy(s2[i].s, hw, sizeof(hw));
+ s2[i].s[sizeof(hw)-2]='0'+i;
+ s2[i].i=i;
+ }
+ tn = tpl_map ("S(si)#", s2, COUNT);
+ tpl_pack (tn, 0);
+ tpl_dump (tn, TPL_FILE, filename);
+ tpl_free (tn);
+ for(i=0; i < COUNT; i++) free(s2[i].s);
+
+ S2 = (s2_t*)calloc (sizeof (s2_t), COUNT);
+ memset(S2, 0xff, sizeof(s2_t)*COUNT);
+ tn = tpl_map ("S(si)#", S2, COUNT);
+ tpl_load (tn, TPL_FILE, filename);
+ tpl_unpack (tn, 0);
+ tpl_free (tn);
+
+ for(i=0; i<COUNT; i++) {
+ printf("%s, %u\n", S2[i].s, S2[i].i);
+ }
+
+
+ /* case 3: */
+ s3 = (s3_t*)calloc (sizeof (s3_t), COUNT);
+ for(i=0; i < COUNT; i++) {
+ memset(s3[i].c, 'a', BUF_SIZE);
+ s3[i].c[BUF_SIZE-1]='\0';
+ s3[i].s = malloc(sizeof(hw));
+ memcpy(s3[i].s, hw, sizeof(hw));
+ s3[i].s[sizeof(hw)-2]='0'+i;
+ s3[i].i=i;
+ }
+ tn = tpl_map ("S(c#si)#", s3, BUF_SIZE, COUNT);
+ tpl_pack (tn, 0);
+ tpl_dump (tn, TPL_FILE, filename);
+ tpl_free (tn);
+
+ S3 = (s3_t*)calloc (sizeof (s3_t), COUNT);
+ memset(S3, 0xff, sizeof(s3_t)*COUNT);
+ tn = tpl_map ("S(c#si)#", S3, BUF_SIZE, COUNT);
+ tpl_load (tn, TPL_FILE, filename);
+ tpl_unpack (tn, 0);
+ tpl_free (tn);
+
+ for(i=0; i<COUNT; i++) {
+ printf("%s, %u\n", S3[i].s, S3[i].i);
+ printf("%s\n", S3[i].c);
+ }
+
+ return 0;
+}
+
--- /dev/null
+hello, world0
+hello, world1
+hello, world2
+hello, world3
+hello, world4
+hello, world5
+hello, world6
+hello, world7
+hello, world8
+hello, world9
+hello, world0, 0
+hello, world1, 1
+hello, world2, 2
+hello, world3, 3
+hello, world4, 4
+hello, world5, 5
+hello, world6, 6
+hello, world7, 7
+hello, world8, 8
+hello, world9, 9
+hello, world0, 0
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world1, 1
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world2, 2
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world3, 3
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world4, 4
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world5, 5
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world6, 6
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world7, 7
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world8, 8
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+hello, world9, 9
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
--- /dev/null
+1 a
+alpha
+beta
+gamma
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+#define NUM_STRS 3
+
+const char *filename = "/tmp/test116.tpl";
+
+int main() {
+ tpl_node *tn;
+ int i,d=1,D=-1;
+ char c='a', C='0';
+ char *strs[NUM_STRS] = {"alpha", "beta", "gamma"};
+ char *STRS[NUM_STRS] = {"femto", "nano", "centi"};
+
+ tn = tpl_map("cs#i", &c, strs, NUM_STRS, &d);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("cs#i", &C, STRS, NUM_STRS, &D);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ printf("%d %c\n", D, C);
+ for(i=0;i<NUM_STRS;i++) printf("%s\n", STRS[i]);
+
+ return 0;
+}
--- /dev/null
+1 a
+alpha
+beta
+gamma
--- /dev/null
+apple
+bpple
+cpple
+apple
+bpple
+cpple
+apple
+bpple
+cpple
+1 a
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tpl.h"
+
+#define NUM_STRS 3
+#define NUM_ELMT 3
+#define STR "apple"
+#define SLEN 5
+
+const char *filename = "/tmp/test117.tpl";
+
+int main() {
+ tpl_node *tn;
+ int i,j,d=1,D=-1;
+ char c='a', C='0';
+ char *strs[NUM_STRS];
+ char *STRS[NUM_STRS];
+
+ tn = tpl_map("cA(s#)i", &c, strs, NUM_STRS, &d);
+ for(i=0; i<NUM_ELMT; i++) { /* pack the same thing this many times*/
+ for(j=0; j<NUM_STRS; j++) {/* each time just tweaking them a bit */
+ strs[j] = malloc( SLEN+1 );
+ memcpy(strs[j], STR, SLEN+1);
+ strs[j][0] = 'a'+j;
+ }
+ tpl_pack(tn,1);
+ }
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("cA(s#)i", &C, STRS, NUM_STRS, &D);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ while(tpl_unpack(tn,1)>0) {
+ for(i=0;i<NUM_STRS;i++) {
+ printf("%s\n", STRS[i]);
+ free(STRS[i]);
+ }
+ }
+ tpl_free(tn);
+
+ printf("%d %c\n", D, C);
+
+ return 0;
+}
--- /dev/null
+apple
+bpple
+cpple
+apple
+bpple
+cpple
+apple
+bpple
+cpple
+1 a
--- /dev/null
+format cA(i#)S(cf#)A(ci#)
+num_fxlens 3
+fxlens[0] 10
+fxlens[1] 5
+fxlens[2] 8
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define ILEN 10
+#define KLEN 8
+#define FLEN 5
+
+struct st {
+ char c;
+ double f[FLEN];
+};
+
+const char *filename = "/tmp/test118.tpl";
+
+int main() {
+ tpl_node *tn;
+ /* some meaningless test data */
+ struct st s = {'z', {0.9, 0.8, 0.7, 0.6, 0.5 }};
+ int j;
+ int i[ILEN] = {-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
+ int k[KLEN] = {100, 200, 300, 400, 500, 600, 700, 800};
+ char a = '&';
+ char b = 'x';
+ const char *fmt;
+ uint32_t num_fxlens, *fxlens;
+
+ tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN);
+ tpl_pack(tn,0);
+
+ tpl_pack(tn,1);
+ for(j=0; j < ILEN; j++) i[j]--;
+ tpl_pack(tn,1);
+ for(j=0; j < ILEN; j++) i[j]--;
+ tpl_pack(tn,1);
+
+ tpl_pack(tn,2);
+ b++;
+ for(j=0; j < KLEN; j++) k[j] += 50;
+ tpl_pack(tn,2);
+ b++;
+ for(j=0; j < KLEN; j++) k[j] += 50;
+ tpl_pack(tn,2);
+
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ /* now peek at the fxlens */
+ fmt = tpl_peek(TPL_FILE|TPL_FXLENS, filename, &num_fxlens, &fxlens);
+ printf("format %s\n", fmt);
+ printf("num_fxlens %u\n", num_fxlens);
+ for(j=0; j<num_fxlens; j++) printf("fxlens[%u] %u\n", j, fxlens[j]);
+ if (num_fxlens>0) free(fxlens);
+ return(0);
+}
--- /dev/null
+format cA(i#)S(cf#)A(ci#)
+num_fxlens 3
+fxlens[0] 10
+fxlens[1] 5
+fxlens[2] 8
--- /dev/null
+size is 1726
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include "tpl.h"
+
+const char *filename = "/tmp/test119.tpl";
+
+#define NUM 100
+
+struct st {
+ int i;
+ char c;
+ double f;
+ uint16_t v[2];
+};
+
+
+int main() {
+ struct st s[NUM], d[NUM];
+ tpl_node *tn;
+ int i;
+ uint32_t sz=0;
+
+ memset(s, 0, sizeof(s)); /* clear s */
+ memset(d, 0, sizeof(d)); /* clear d */
+
+ /* fill s with random stuff */
+ for(i=0; i < NUM; i++) {
+ s[i].i = i; s[i].c='a'+i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i;
+ }
+
+ tn = tpl_map("S(icfv#)#", s, 2, NUM);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_GETSIZE,&sz);
+ tpl_free(tn);
+
+ printf("size is %u\n", sz);
+
+ return 0;
+}
+
--- /dev/null
+size is 1726
--- /dev/null
+test12.tpl: not a valid tpl file
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test12.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test12.tpl: not a valid tpl file
--- /dev/null
+testing undersized output buffer... -1
+testing sufficient output buffer... 0
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i,rc,j;
+ char toosmall[10];
+ char buf[60];
+
+ tn = tpl_map("A(i)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+ rc=tpl_dump(tn,TPL_MEM|TPL_PREALLOCD,toosmall,sizeof(toosmall));
+ printf("testing undersized output buffer... %d \n", rc);
+ rc=tpl_dump(tn,TPL_MEM|TPL_PREALLOCD,buf,sizeof(buf));
+ printf("testing sufficient output buffer... %d \n", rc);
+ tpl_free(tn);
+
+ tn = tpl_map("A(i)",&j);
+ tpl_load(tn,TPL_MEM|TPL_EXCESS_OK,buf,sizeof(buf));
+ while (tpl_unpack(tn,1) > 0) printf("j is %d\n", j);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+tpl_dump: buffer too small, need 57 bytes
--- /dev/null
+testing undersized output buffer... -1
+testing sufficient output buffer... 0
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
--- /dev/null
+one
+two
+three
+eins
+zwei
+drei
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+const char *filename = "/tmp/test121.tpl";
+int main() {
+ char *labels[2][3] = { {"one", "two", "three"},
+ {"eins", "zwei", "drei" } };
+ char *olabels[2][3] = { {NULL,NULL,NULL }, {NULL,NULL,NULL}};
+ int i,j;
+
+ tpl_node *tn;
+ tn = tpl_map("s##", labels, 2, 3);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ tn = tpl_map("s##", olabels, 2, 3);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ for(i=0;i<2;i++) {
+ for(j=0;j<3;j++) {
+ printf("%s\n", olabels[i][j]);
+ free(olabels[i][j]);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+one
+two
+three
+eins
+zwei
+drei
--- /dev/null
+1 abc 3.140000 a 1
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "tpl.h"
+
+const char *filename = "/tmp/test122.tpl";
+
+typedef struct {
+ char c;
+ int i;
+} inner_t;
+
+typedef struct {
+ int i;
+ char c[3];
+ double f;
+ inner_t inner;
+} outer;
+
+int main() {
+ tpl_node *tn;
+ outer ms = {1, {'a','b','c'}, 3.14, {'a',1}};
+ outer os;
+
+ tn = tpl_map( "S(ic#f$(ci))", &ms, 3);
+ tpl_pack( tn, 0 );
+ tpl_dump( tn, TPL_FILE, filename );
+ tpl_free( tn );
+
+ memset(&os, 0, sizeof(outer));
+ tn = tpl_map( "S(ic#f$(ci))", &os, 3);
+ tpl_load( tn, TPL_FILE, filename );
+ tpl_unpack( tn, 0 );
+ tpl_free( tn );
+
+ printf("%d %c%c%c %f %c %d\n", os.i, os.c[0],os.c[1],os.c[2],os.f,
+ os.inner.c, os.inner.i);
+
+ return(0);
+}
--- /dev/null
+1 abc 3.140000 a 1
--- /dev/null
+caught error!
--- /dev/null
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "tpl.h"
+
+jmp_buf env;
+extern tpl_hook_t tpl_hook;
+
+int catch_oops(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ longjmp(env,-1); /* return to setjmp point */
+ return 0; /* not reached */
+}
+
+int main() {
+ int err;
+ tpl_node *tn;
+ tpl_hook.oops = catch_oops; /* install fatal handler */
+
+ err = setjmp(env); /* on error, control will return here */
+ if (err) {
+ printf("caught error!\n");
+ return -1;
+ }
+
+ tn = tpl_map("@"); /* generate a fatal error */
+ printf("program ending, without error\n");
+ return 0;
+}
--- /dev/null
+unsupported option @
--- /dev/null
+caught error!
--- /dev/null
+mapped
+freed
+abcdefghi first
+jklmnopqr second
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "tpl.h"
+#define LEN 10
+
+const char *filename = "/tmp/test124.tpl";
+
+typedef struct {
+ char name[LEN];
+} test_t;
+int main() {
+ test_t t;
+ char *s;
+ tpl_node *tn;
+
+ tn = tpl_map("A(S(c#)s)", &t, LEN, &s);
+ printf("mapped\n");
+
+ memcpy(t.name,"abcdefghi\0",10);
+ s="first";
+ tpl_pack(tn,1);
+
+ memcpy(t.name,"jklmnopqr\0",10);
+ s="second";
+ tpl_pack(tn,1);
+
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+ printf("freed\n");
+
+ tn = tpl_map("A(S(c#)s)", &t, LEN, &s);
+ tpl_load(tn,TPL_FILE,filename);
+ while(tpl_unpack(tn,1) > 0) {
+ printf("%s %s\n", t.name, s);
+ }
+ tpl_free(tn);
+ return 0;
+}
--- /dev/null
+mapped
+freed
+abcdefghi first
+jklmnopqr second
--- /dev/null
+test13.tpl: not a valid tpl file
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test13.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test13.tpl: not a valid tpl file
--- /dev/null
+test14.tpl: not a valid tpl file
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test14.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test14.tpl: not a valid tpl file
--- /dev/null
+test15.tpl: not a valid tpl file
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test15.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test15.tpl: not a valid tpl file
--- /dev/null
+test16.tpl: not a valid tpl file
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test16.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test16.tpl: not a valid tpl file
--- /dev/null
+test17.tpl: format signature mismatch
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(c)",&i);
+ tpl_load(tn,TPL_FILE,"test17.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test17.tpl: format signature mismatch
--- /dev/null
+failed to parse A(i
+tpl map failed
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i=-1;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i",&i);
+ printf("tpl map %s\n", tn ? "succeeded" : "failed");
+ return(0);
+}
--- /dev/null
+failed to parse A(i
+tpl map failed
--- /dev/null
+failed to parse A(i))
+tpl map failed
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i=-1;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i))",&i);
+ printf("tpl map %s\n", tn ? "succeeded" : "failed");
+ return(0);
+}
--- /dev/null
+failed to parse A(i))
+tpl map failed
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i,j=-1;
+
+ tn = tpl_map("i",&i);
+ i=1;
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test2.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("i",&j);
+ tpl_load(tn,TPL_FILE,"/tmp/test2.tpl");
+ tpl_unpack(tn,0);
+ printf("j is %d\n", j);
+ tpl_free(tn);
+ return(0);
+
+}
--- /dev/null
+failed to parse iA()
+tpl map failed
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("iA()",&i);
+ printf("tpl map %s\n", tn ? "succeeded" : "failed");
+ return(0);
+}
--- /dev/null
+failed to parse iA()
+tpl map failed
--- /dev/null
+i,j are 0,1
+i,j are 2,3
+i,j are 4,5
+i,j are 6,7
+i,j are 8,9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i,j=-1;
+
+ tn = tpl_map("A(ii)",&i,&j);
+ for(i=0,j=1;i<10;i+=2,j+=2) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"/tmp/test21.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(ii)",&i,&j);
+ tpl_load(tn,TPL_FILE,"/tmp/test21.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i,j are %d,%d\n", i,j);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i,j are 0,1
+i,j are 2,3
+i,j are 4,5
+i,j are 6,7
+i,j are 8,9
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ unsigned i;
+
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"/tmp/test22.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn,TPL_FILE,"/tmp/test22.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main() {
+ tpl_node *tn;
+ unsigned i;
+ char *file = "/tmp/test23.tpl";
+ int fd;
+
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE, file);
+ tpl_free(tn);
+
+ if ( (fd=open( file,O_RDONLY)) == -1) {
+ printf("failed to open %s: %s", file, strerror(errno));
+ }
+
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_FD, fd);
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main() {
+ tpl_node *tn;
+ unsigned i;
+ char *file = "test24.tpl";
+ int fd;
+
+ if ( (fd=open( file,O_RDONLY)) == -1) {
+ printf("failed to open %s: %s", file, strerror(errno));
+ exit(-1);
+ }
+
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_FD, fd);
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+test25.tpl: not a valid tpl file
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ unsigned i;
+ char *file = "test25.tpl";
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(u)",&i);
+ if (tpl_load(tn, TPL_FILE, file) < 0 ) {
+ tpl_free(tn);
+ exit(-1);
+ }
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test25.tpl: not a valid tpl file
--- /dev/null
+3 tpls gathered.
+999045 is their sum.
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include "tpl.h"
+
+#define DEBUG 0
+
+int num_tpls = 0, sum_tpls = 0;
+
+int tpl_cb(void *tpl, size_t tpllen, void*data) {
+ int i;
+ tpl_node *tn;
+
+ if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen);
+ tn = tpl_map("A(i)", &i);
+ tpl_load(tn, TPL_MEM, tpl, tpllen);
+ num_tpls++;
+ while (tpl_unpack(tn,1) > 0) sum_tpls += i;
+ tpl_free(tn);
+ return 0;
+
+}
+
+int main() {
+ FILE *f1,*f2;
+ int fdflags,fd,fd1,fd2;
+ int selrc, maxfd;
+ tpl_gather_t *gs1=NULL,*gs2=NULL,**gs;
+
+ struct timeval tv;
+ fd_set rset;
+
+ f1 = popen("cat test26_0.tpl;sleep 1; cat test26_1.tpl", "r");
+ fd1 = fileno(f1);
+ fdflags = fcntl(fd1, F_GETFL, 0);
+ fcntl( fd1, F_SETFL, fdflags | O_NONBLOCK);
+
+ f2 = popen("cat test26_2.tpl;sleep 1; cat test26_3.tpl", "r");
+ fd2 = fileno(f2);
+ fdflags = fcntl(fd2, F_GETFL, 0);
+ fcntl( fd2, F_SETFL, fdflags | O_NONBLOCK);
+
+ while (1) {
+ FD_ZERO( &rset );
+ if (fd1 >= 0) FD_SET( fd1, &rset );
+ if (fd2 >= 0) FD_SET( fd2, &rset );
+
+ if (fd1 == -1 && fd2 == -1) {
+ printf("%d tpls gathered.\n",num_tpls);
+ printf("%d is their sum.\n",sum_tpls);
+ return(0);
+ }
+
+ maxfd=0;
+ if (fd1>maxfd) maxfd = fd1;
+ if (fd2>maxfd) maxfd = fd2;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ selrc = select(maxfd+1, &rset, NULL, NULL, &tv );
+ if (selrc == -1) {
+ perror("select()");
+ } else if (selrc) {
+ for(fd=0;fd<maxfd+1;fd++) {
+ if ( FD_ISSET(fd, &rset) ) {
+ if (DEBUG) printf("fd %d readable\n", fd);
+ gs = (fd1 == fd) ? &gs1 : &gs2;
+ if (tpl_gather(TPL_GATHER_NONBLOCKING,fd,gs,tpl_cb,NULL) <= 0) {
+ if (fd1 == fd) {pclose(f1); fd1 = -1; }
+ if (fd2 == fd) {pclose(f2); fd2 = -1; }
+ } else {
+ if (DEBUG) printf("tpl_gather >0\n");
+ }
+ }
+ }
+ } else {
+ if (DEBUG) printf("timeout\n");
+ }
+ }
+ return(0);
+}
--- /dev/null
+3 tpls gathered.
+999045 is their sum.
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main() {
+ tpl_node *tn;
+ unsigned i,perms;
+ char *file = "/tmp/test27.tpl";
+ int fd;
+
+ perms = S_IRUSR|S_IWUSR;
+ if ( (fd=open( file,O_RDWR|O_CREAT|O_TRUNC,perms)) == -1) {
+ printf("failed to open %s: %s", file, strerror(errno));
+ exit(-1);
+ }
+
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FD, fd);
+ tpl_free(tn);
+
+ lseek(fd,0,SEEK_SET); /* re-position fd to start of file */
+
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_FD, fd);
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+sum is 49995000
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main() {
+ tpl_node *tn;
+ unsigned i, sum=0;
+ int fd[2], pid;
+
+ pipe(fd);
+ if ( (pid = fork()) == 0) { /* child */
+
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_FD, fd[0]);
+ while (tpl_unpack(tn,1) > 0) sum += i;
+ tpl_free(tn);
+ printf("sum is %d\n", sum);
+
+ } else if (pid > 0) { /* parent */
+
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10000;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FD, fd[1] );
+ tpl_free(tn);
+
+ waitpid(pid,NULL,0);
+
+ } else if (pid == -1) {
+ perror("fork error");
+ }
+ return(0);
+}
--- /dev/null
+sum is 49995000
--- /dev/null
+sum is 49995000
+abcdefghijklmnopqrstuvwxyz
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main() {
+ tpl_node *tn;
+ unsigned i, sum=0;
+ int fd[2], pid;
+ char c;
+
+ pipe(fd);
+ if ( (pid = fork()) == 0) { /* child */
+
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_FD, fd[0]);
+ while (tpl_unpack(tn,1) > 0) sum += i;
+ tpl_free(tn);
+ printf("sum is %d\n", sum);
+
+ tn = tpl_map("A(c)",&c);
+ tpl_load(tn, TPL_FD, fd[0]);
+ while (tpl_unpack(tn,1) > 0) printf("%c",c);
+ tpl_free(tn);
+ printf("\n");
+
+ } else if (pid > 0) { /* parent */
+
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10000;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FD, fd[1] );
+ tpl_free(tn);
+
+ tn = tpl_map("A(c)",&c);
+ for(c='a';c<='z';c++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FD, fd[1] );
+ tpl_free(tn);
+
+ waitpid(pid,NULL,0);
+
+ } else if (pid == -1) {
+ perror("fork error");
+ }
+ return(0);
+}
--- /dev/null
+sum is 49995000
+abcdefghijklmnopqrstuvwxyz
--- /dev/null
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i,j=-1;
+
+ tn = tpl_map("A(i)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"/tmp/test3.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(i)",&j);
+ tpl_load(tn,TPL_FILE,"/tmp/test3.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("j is %d\n", j);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
--- /dev/null
+buffer length: 4
+good
--- /dev/null
+#include "tpl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char*argv[]) {
+ tpl_bin bin;
+ tpl_node *tn;
+ int i;
+ char *file = "/tmp/test30.tpl";
+ char str[10];
+
+ strcpy(str,"good egg");
+ bin.addr = str;
+ bin.sz = 4; /* just going to pack 'good' (no NUL) */
+
+ tn = tpl_map("B", &bin);
+ tpl_pack(tn,0);
+ memset(str,0,10); /* just to test that buf was copied */
+ tpl_dump(tn,TPL_FILE,file);
+ tpl_free(tn);
+
+ bin.addr = NULL;
+ bin.sz = 0;
+
+ tn = tpl_map("B", &bin);
+ tpl_load(tn,TPL_FILE,file);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ /* print the buffer char-by-char ; its not a nul-termd string */
+ printf("buffer length: %u\n", bin.sz);
+ for(i=0; i < bin.sz; i++) printf("%c", ((char*)bin.addr)[i]);
+ printf("\n");
+
+ if (bin.sz > 0)
+ free(bin.addr); /* malloc'd for us by tpl_unpack, we must free */
+ return(0);
+}
+
--- /dev/null
+buffer length: 4
+good
--- /dev/null
+buffer length: 0
+
--- /dev/null
+#include "tpl.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char*argv[]) {
+ tpl_bin bin;
+ tpl_node *tn;
+ int i;
+ char *file = "/tmp/test31.tpl";
+
+ bin.addr = NULL;
+ bin.sz = 0;
+
+ tn = tpl_map("B", &bin);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,file);
+ tpl_free(tn);
+
+ /* load these two fields with bogus values to test that tpl_unpack
+ * sets them back to NULL, and 0 respectively. */
+ bin.addr = file;
+ bin.sz = 4;
+
+ tn = tpl_map("B", &bin);
+ tpl_load(tn,TPL_FILE,file);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ /* print the buffer char-by-char ; its not a nul-termd string */
+ printf("buffer length: %u\n", bin.sz);
+ for(i=0; i < bin.sz; i++) printf("%c", ((char*)bin.addr)[i]);
+ printf("\n");
+
+ if (bin.sz > 0)
+ free(bin.addr); /* malloc'd for us by tpl_unpack, we must free */
+ return(0);
+}
+
--- /dev/null
+buffer length: 0
+
--- /dev/null
+buffer length: 4
+good
+buffer length: 4
+ood
+buffer length: 4
+od e
+buffer length: 4
+d eg
+buffer length: 4
+ egg
--- /dev/null
+#include "tpl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char*argv[]) {
+ tpl_bin bin;
+ tpl_node *tn;
+ int i;
+ char *file = "/tmp/test32.tpl";
+ char str[10];
+
+ strcpy(str,"good egg");
+ bin.addr = str;
+ bin.sz = 4; /* just going to pack 'good' (no NUL) */
+
+ tn = tpl_map("A(B)", &bin);
+ for(i=0; i < 5; i++) {
+ tpl_pack(tn,1);
+ bin.addr = (char*)(bin.addr) + 1;
+ }
+ memset(str,0,10); /* just to test that buf was copied */
+ tpl_dump(tn,TPL_FILE,file);
+ tpl_free(tn);
+
+ bin.addr = NULL;
+ bin.sz = 0;
+
+ tn = tpl_map("A(B)", &bin);
+ tpl_load(tn,TPL_FILE,file);
+ while (tpl_unpack(tn,1) > 0) {
+ /* print the buffer char-by-char ; its not a nul-termd string */
+ printf("buffer length: %u\n", bin.sz);
+ for(i=0; i < bin.sz; i++) printf("%c", ((char*)bin.addr)[i]);
+ printf("\n");
+
+ if (bin.sz > 0)
+ free(bin.addr); /* malloc'd for us by tpl_unpack, we must free */
+ }
+ tpl_free(tn);
+ return(0);
+
+}
+
--- /dev/null
+buffer length: 4
+good
+buffer length: 4
+ood
+buffer length: 4
+od e
+buffer length: 4
+d eg
+buffer length: 4
+ egg
--- /dev/null
+sizeof(double) is 8
+y is 1.000000
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ double x,y;
+
+ printf("sizeof(double) is %d\n", (int)sizeof(double));
+
+ tn = tpl_map("f",&x);
+ x=1.0;
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test33.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("f",&y);
+ tpl_load(tn,TPL_FILE,"/tmp/test33.tpl");
+ tpl_unpack(tn,0);
+ printf("y is %.6f\n", y);
+ tpl_free(tn);
+
+ return(0);
+}
--- /dev/null
+sizeof(double) is 8
+y is 1.000000
--- /dev/null
+sizeof(double) is 8
+y is 1.000000
+y is 1.666667
+y is 2.333333
+y is 3.000000
+y is 3.666667
+y is 4.333333
+y is 5.000000
+y is 5.666667
+y is 6.333333
+y is 7.000000
+y is 7.666667
+y is 8.333333
+y is 9.000000
+y is 9.666667
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ double x,y;
+
+ printf("sizeof(double) is %d\n", (int)sizeof(double));
+
+ tn = tpl_map("A(f)",&x);
+ for( x=1.0; x < 10.0; x += 2/3.0) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"/tmp/test34.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(f)",&y);
+ tpl_load(tn,TPL_FILE,"/tmp/test34.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("y is %.6f\n", y);
+ tpl_free(tn);
+
+ return(0);
+}
--- /dev/null
+sizeof(double) is 8
+y is 1.000000
+y is 1.666667
+y is 2.333333
+y is 3.000000
+y is 3.666667
+y is 4.333333
+y is 5.000000
+y is 5.666667
+y is 6.333333
+y is 7.000000
+y is 7.666667
+y is 8.333333
+y is 9.000000
+y is 9.666667
--- /dev/null
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ int id;
+ char *name, *names[] = { "joe", "bob", "cary" };
+
+ tn = tpl_map("A(is)", &id, &name);
+
+ for(id=0,name=names[id]; id < 3; name=names[++id])
+ tpl_pack(tn,1);
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test35.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+id 0, user joe
+id 1, user bob
+id 2, user cary
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ int id;
+ char *name;
+
+ tn = tpl_map("A(is)", &id, &name);
+ tpl_load(tn, TPL_FILE, "/tmp/test35.tpl");
+
+ while ( tpl_unpack(tn,1) > 0 ) {
+ printf("id %d, user %s\n", id, name);
+ free(name);
+ }
+
+ tpl_free(tn);
+ return(0);
+}
+
+
--- /dev/null
+id 0, user joe
+id 1, user bob
+id 2, user cary
--- /dev/null
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i,j;
+
+ tn = tpl_map("A(A(i))",&j);
+
+ for(i=2;i<4;i++) {
+
+ for(j=i; j < 10*i; j *= i) {
+ tpl_pack(tn,2);
+ }
+ tpl_pack(tn,1);
+ }
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test37.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+unpacking index 1:
+ unpacking index 2: j is 2
+ unpacking index 2: j is 4
+ unpacking index 2: j is 8
+ unpacking index 2: j is 16
+unpacking index 1:
+ unpacking index 2: j is 3
+ unpacking index 2: j is 9
+ unpacking index 2: j is 27
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int j;
+
+ tn = tpl_map("A(A(i))",&j);
+ tpl_load(tn,TPL_FILE, "/tmp/test37.tpl");
+
+ while (tpl_unpack(tn,1) > 0) {
+ printf("unpacking index 1:\n");
+ while (tpl_unpack(tn,2) > 0) {
+ printf(" unpacking index 2: j is %d\n", j);
+ }
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+unpacking index 1:
+ unpacking index 2: j is 2
+ unpacking index 2: j is 4
+ unpacking index 2: j is 8
+ unpacking index 2: j is 16
+unpacking index 1:
+ unpacking index 2: j is 3
+ unpacking index 2: j is 9
+ unpacking index 2: j is 27
--- /dev/null
+test39.tpl: not a valid tpl file
+load failed (rc=-1)
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i, rc;
+
+ tpl_hook.oops = printf;
+
+ tn = tpl_map("A(i)",&i);
+ rc = tpl_load(tn,TPL_FILE,"test39.tpl");
+ printf("load %s (rc=%d)\n", (rc >= 0 ? "ok" : "failed"), rc);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+test39.tpl: not a valid tpl file
+load failed (rc=-1)
--- /dev/null
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i,j=-1;
+ void *addr;
+ int sz;
+
+ tn = tpl_map("A(i)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_MEM,&addr,&sz);
+ tpl_free(tn);
+
+ tn = tpl_map("A(i)",&j);
+ tpl_load(tn,TPL_MEM,addr,sz);
+ while (tpl_unpack(tn,1) > 0) printf("j is %d\n", j);
+ tpl_free(tn);
+ free(addr);
+ return(0);
+}
--- /dev/null
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
--- /dev/null
+#include "tpl.h"
+
+int main() {
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(A(c))", &c);
+
+ for(c='a'; c<'c'; c++) tpl_pack(tn,2);
+ tpl_pack(tn, 1);
+
+ for(c='1'; c<'4'; c++) tpl_pack(tn,2);
+ tpl_pack(tn, 1);
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test40.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+a b
+1 2 3
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+int main() {
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(A(c))", &c);
+
+ tpl_load(tn, TPL_FILE, "/tmp/test40.tpl");
+ while (tpl_unpack(tn,1) > 0) {
+ while (tpl_unpack(tn,2) > 0) printf("%c ",c);
+ printf("\n");
+ }
+ tpl_free(tn);
+ return(0);
+}
+
--- /dev/null
+a b
+1 2 3
--- /dev/null
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ char id;
+ char *name, *names[] = { "joe", "bob", "cary" };
+
+ tn = tpl_map("A(cs)", &id, &name);
+
+ for(id=0,name=names[(int)id]; id < 3; name=names[(int)++id])
+ tpl_pack(tn,1);
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test42.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ char id;
+ tpl_bin bin;
+
+ char *junk = "0123456789";
+ bin.sz = 10;
+ bin.addr = junk;
+
+ tn = tpl_map("A(cB)", &id, &bin);
+
+ for(id=0; id < 3; ++id)
+ tpl_pack(tn,1); /* pack same bin buffer, doesn't matter */
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test43.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+ tpl_node *tn;
+ char id,j;
+ tpl_bin bin;
+
+ char *junk = "0123456789";
+ bin.sz = 10;
+ bin.addr = junk;
+
+ tn = tpl_map("A(cA(B))", &id, &bin);
+
+ for(id=0; id < 3; ++id) {
+ for(j=0;j<2;j++)
+ tpl_pack(tn,2); /* pack same bin buffer, doesn't matter */
+ tpl_pack(tn,1);
+ }
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test44.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+x is 0.500000
+y is 0.500000
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ double x=0.5,y=0.0;
+
+ tn = tpl_map("f",&x);
+ tpl_pack(tn,0);
+ printf("x is %f\n", x);
+ tpl_dump(tn,TPL_FILE,"/tmp/test45.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("f",&y);
+ tpl_load(tn,TPL_FILE,"/tmp/test45.tpl");
+ tpl_unpack(tn,0);
+ printf("y is %f\n", y);
+ tpl_free(tn);
+ return(0);
+
+}
--- /dev/null
+x is 0.500000
+y is 0.500000
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ double x;
+
+ tn = tpl_map("A(f)",&x);
+ for(x=0.0;x<1.0;x+=0.2) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"/tmp/test46.tpl");
+ tpl_free(tn);
+
+ return(0);
+}
--- /dev/null
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map( "A(i)", &i );
+ for( i=0; i<10; i++ ) {
+ tpl_pack( tn, 1 );
+ }
+ tpl_dump( tn, TPL_FILE, "/tmp/test47.tpl" );
+ tpl_free( tn );
+ return(0);
+}
--- /dev/null
+0 1 2 3 4 5 6 7 8 9
\ No newline at end of file
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map( "A(i)", &i );
+ tpl_load( tn, TPL_FILE, "/tmp/test47.tpl" );
+ while (tpl_unpack( tn, 1 ) > 0) {
+ printf("%d ", i);
+ }
+ tpl_free( tn );
+ return(0);
+}
--- /dev/null
+0 1 2 3 4 5 6 7 8 9
\ No newline at end of file
--- /dev/null
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ char *s;
+
+ tn = tpl_map( "A(s)", &s );
+
+ s = "bob";
+ tpl_pack(tn, 1);
+
+ s = "betty";
+ tpl_pack(tn, 1);
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test49.tpl");
+ tpl_free(tn);
+ return(0);
+ }
--- /dev/null
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 16
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 16
+j is 17
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 16
+j is 17
+j is 18
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int o,i,j=-1;
+ void *addr;
+ int sz;
+
+ tn = tpl_map("A(A(i))",&i);
+ for(o=0;o<10;o++) {
+ for(i=o; i < o+10; i++) tpl_pack(tn,2);
+ tpl_pack(tn,1);
+ }
+ tpl_dump(tn,TPL_MEM,&addr,&sz);
+ tpl_free(tn);
+
+ tn = tpl_map("A(A(i))",&j);
+ tpl_load(tn,TPL_MEM,addr,sz);
+ while (tpl_unpack(tn,1) > 0) {
+ while (tpl_unpack(tn,2) > 0) printf("j is %d\n", j);
+ }
+ tpl_free(tn);
+ free(addr);
+ return(0);
+}
--- /dev/null
+j is 0
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 1
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 2
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 3
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 4
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 5
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 6
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 7
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 16
+j is 8
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 16
+j is 17
+j is 9
+j is 10
+j is 11
+j is 12
+j is 13
+j is 14
+j is 15
+j is 16
+j is 17
+j is 18
--- /dev/null
+bob
+betty
--- /dev/null
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include "tpl.h"
+
+ int main() {
+ tpl_node *tn;
+ char *s;
+
+ tn = tpl_map( "A(s)", &s );
+ tpl_load( tn, TPL_FILE, "/tmp/test49.tpl" );
+
+ while (tpl_unpack( tn, 1 ) > 0) {
+ printf("%s\n", s);
+ free(s); /* important! */
+ }
+
+ tpl_free(tn);
+ return(0);
+ }
--- /dev/null
+bob
+betty
--- /dev/null
+num_tpls: 4, sum: 180
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "tpl.h"
+
+#define DEBUG 0
+#define FILE_BUFLEN 500
+
+int num_tpls = 0, sum_tpls = 0;
+
+int tpl_cb(void *tpl, size_t tpllen, void*data) {
+ int i;
+ tpl_node *tn;
+
+ if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen);
+ tn = tpl_map("A(i)", &i);
+ tpl_load(tn, TPL_MEM, tpl, tpllen);
+ num_tpls++;
+ while (tpl_unpack(tn,1) > 0) sum_tpls += i;
+ tpl_free(tn);
+ return 0;
+
+}
+
+int main(int argc, char *argv[]) {
+ char *files[] = {"test51_0.tpl", "test51_1.tpl", "test51_2.tpl", "test51_3.tpl","test51_4.tpl", NULL};
+ char **f;
+ char buf[FILE_BUFLEN];
+ int rc,fd;
+ tpl_gather_t *gs=NULL;
+
+ for (f = files; *f; f++) {
+ if (DEBUG) printf("file is %s\n", *f);
+ if ( ( fd = open(*f, O_RDONLY) ) == -1) {
+ printf("error - can't open %s: %s\n", *f, strerror(errno));
+ exit(-1);
+ }
+ rc = read(fd,&buf,FILE_BUFLEN); /* read whole file (no points for style) */
+ if (rc == -1) {
+ printf("error - can't read %s: %s\n", *f, strerror(errno));
+ exit(-1);
+ }
+ if (tpl_gather(TPL_GATHER_MEM,buf, rc, &gs, tpl_cb, NULL) <= 0) {
+ printf("tpl_gather_mem returned <= 0, exiting\n");
+ exit(-1);
+ }
+ close(fd);
+ }
+ printf("num_tpls: %d, sum: %d\n", num_tpls, sum_tpls);
+ return(0);
+}
--- /dev/null
+num_tpls: 4, sum: 180
--- /dev/null
+----------
+--> j is 1
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int j;
+
+ tn = tpl_map("A(A(i))",&j);
+
+ j=1;
+ tpl_pack(tn,2);
+ tpl_pack(tn,1);
+ j=2;
+ tpl_pack(tn,2);
+ /* omit packing parent */
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test52.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(A(i))",&j);
+ tpl_load(tn, TPL_FILE, "/tmp/test52.tpl");
+ while(tpl_unpack(tn,1) > 0) {
+ printf("----------\n");
+ while(tpl_unpack(tn,2) > 0) {
+ printf("--> j is %d\n", j);
+ }
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+----------
+--> j is 1
--- /dev/null
+----------
+--> j is 1
+----------
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int j;
+
+ tn = tpl_map("A(A(i))",&j);
+
+ j=1;
+ tpl_pack(tn,2);
+ tpl_pack(tn,1);
+ /* j=2; */
+ /* tpl_pack(tn,2); */
+ tpl_pack(tn,1); /* pack zero-length nested array */
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test53.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(A(i))",&j);
+ tpl_load(tn, TPL_FILE, "/tmp/test53.tpl");
+ while(tpl_unpack(tn,1) > 0) {
+ printf("----------\n");
+ while(tpl_unpack(tn,2) > 0) {
+ printf("--> j is %d\n", j);
+ }
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+----------
+--> j is 1
+----------
--- /dev/null
+tpl_mem_gather aborted by app callback
+tpl_gather_mem returned <= 0, exiting
--- /dev/null
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "tpl.h"
+
+#define DEBUG 0
+#define FILE_BUFLEN 500
+
+extern tpl_hook_t tpl_hook;
+int num_tpls = 0, sum_tpls = 0;
+
+int tpl_cb(void *tpl, size_t tpllen, void*data) {
+ int i;
+ tpl_node *tn;
+
+ tpl_hook.oops = printf;
+
+ if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen);
+ tn = tpl_map("A(i)", &i);
+ tpl_load(tn, TPL_MEM, tpl, tpllen);
+ num_tpls++;
+ while (tpl_unpack(tn,1) > 0) sum_tpls += i;
+ tpl_free(tn);
+ /* this next line is a hack to test the callback's ability
+ * to abort further tpl processing by returning < 0 */
+ if (num_tpls == 3) return -1;
+ return 0;
+
+}
+
+int main(int argc, char *argv[]) {
+ char *files[] = {"test54_0.tpl", "test54_1.tpl", "test54_2.tpl", "test54_3.tpl","test54_4.tpl", NULL};
+ char **f;
+ char buf[FILE_BUFLEN];
+ int rc,fd;
+ tpl_gather_t *gs=NULL;
+
+ for (f = files; *f; f++) {
+ if (DEBUG) printf("file is %s\n", *f);
+ if ( ( fd = open(*f, O_RDONLY) ) == -1) {
+ printf("error - can't open %s: %s\n", *f, strerror(errno));
+ exit(-1);
+ }
+ rc = read(fd,&buf,FILE_BUFLEN); /* read whole file (no points for style) */
+ if (rc == -1) {
+ printf("error - can't read %s: %s\n", *f, strerror(errno));
+ exit(-1);
+ }
+ if (tpl_gather(TPL_GATHER_MEM,buf, rc, &gs, tpl_cb, NULL) <= 0) {
+ printf("tpl_gather_mem returned <= 0, exiting\n");
+ exit(-1);
+ }
+ close(fd);
+ }
+ printf("num_tpls: %d, sum: %d\n", num_tpls, sum_tpls);
+ return(0);
+}
--- /dev/null
+tpl_mem_gather aborted by app callback
+tpl_gather_mem returned <= 0, exiting
--- /dev/null
+tpl_fd_gather aborted by app callback
+2 tpls gathered.
+499545 is their sum.
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include "tpl.h"
+
+#define DEBUG 0
+
+extern tpl_hook_t tpl_hook;
+int num_tpls = 0, sum_tpls = 0;
+
+int tpl_cb(void *tpl, size_t tpllen, void*data) {
+ int i;
+ tpl_node *tn;
+
+ tpl_hook.oops = printf;
+
+ if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen);
+ tn = tpl_map("A(i)", &i);
+ tpl_load(tn, TPL_MEM, tpl, tpllen);
+ num_tpls++;
+ while (tpl_unpack(tn,1) > 0) sum_tpls += i;
+ tpl_free(tn);
+ /* this next line is a hack to test the callback's ability
+ * to abort further tpl processing by returning < 0 */
+ if (num_tpls == 1) return -1;
+ return 0;
+
+}
+
+int main() {
+ FILE *f1,*f2;
+ int fdflags,fd,fd1,fd2;
+ int selrc, maxfd;
+ tpl_gather_t *gs1=NULL,*gs2=NULL,**gs;
+ struct timeval tv;
+ fd_set rset;
+
+
+ f1 = popen("cat test26_0.tpl;sleep 1; cat test26_1.tpl", "r");
+ fd1 = fileno(f1);
+ fdflags = fcntl(fd1, F_GETFL, 0);
+ fcntl( fd1, F_SETFL, fdflags | O_NONBLOCK);
+
+ f2 = popen("cat test26_2.tpl;sleep 1; cat test26_3.tpl", "r");
+ fd2 = fileno(f2);
+ fdflags = fcntl(fd2, F_GETFL, 0);
+ fcntl( fd2, F_SETFL, fdflags | O_NONBLOCK);
+
+ while (1) {
+ FD_ZERO( &rset );
+ if (fd1 >= 0) FD_SET( fd1, &rset );
+ if (fd2 >= 0) FD_SET( fd2, &rset );
+
+ if (fd1 == -1 && fd2 == -1) {
+ printf("%d tpls gathered.\n",num_tpls);
+ printf("%d is their sum.\n",sum_tpls);
+ return(0);
+ }
+
+ maxfd=0;
+ if (fd1>maxfd) maxfd = fd1;
+ if (fd2>maxfd) maxfd = fd2;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ selrc = select(maxfd+1, &rset, NULL, NULL, &tv );
+ if (selrc == -1) {
+ perror("select()");
+ } else if (selrc) {
+ for(fd=0;fd<maxfd+1;fd++) {
+ if ( FD_ISSET(fd, &rset) ) {
+ if (DEBUG) printf("fd %d readable\n", fd);
+ gs = (fd1 == fd) ? &gs1 : &gs2;
+ if (tpl_gather(TPL_GATHER_NONBLOCKING,fd,gs,tpl_cb,NULL) <= 0) {
+ if (fd1 == fd) {pclose(f1); fd1 = -1; }
+ if (fd2 == fd) {pclose(f2); fd2 = -1; }
+ } else {
+ if (DEBUG) printf("tpl_gather >0\n");
+ }
+ }
+ }
+ } else {
+ if (DEBUG) printf("timeout\n");
+ }
+ }
+ return(0);
+}
--- /dev/null
+tpl_fd_gather aborted by app callback
+2 tpls gathered.
+499545 is their sum.
--- /dev/null
+hs2.s1 length: 5
+hs2.s1: draco
+hs2.s2 length: 2
+hs2.s2: po
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define S1_LEN 6
+#define S2_LEN 4
+
+struct has_strings {
+ char a;
+ char s1[S1_LEN];
+ char s2[S2_LEN];
+};
+
+int main(int argc,char*argv[]) {
+ tpl_node *tn;
+ struct has_strings hs,hs2;
+ void *img;
+ size_t sz;
+
+ strncpy(hs.s1, "draco",S1_LEN);
+ strncpy(hs.s2, "po",S2_LEN);
+
+ tn = tpl_map("c#c#", hs.s1, S1_LEN, hs.s2, S2_LEN);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_MEM,&img,&sz);
+ tpl_free(tn);
+
+ /* unpack */
+
+ tn = tpl_map("c#c#", hs2.s1, S1_LEN, hs2.s2, S2_LEN);
+ tpl_load(tn,TPL_MEM,img,sz);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ free(img);
+
+ printf("hs2.s1 length: %d\n", (int)strlen(hs2.s1));
+ printf("hs2.s1: %s\n", hs2.s1);
+ printf("hs2.s2 length: %d\n", (int)strlen(hs2.s2));
+ printf("hs2.s2: %s\n", hs2.s2);
+ return(0);
+}
--- /dev/null
+hs2.s1 length: 5
+hs2.s1: draco
+hs2.s2 length: 2
+hs2.s2: po
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define S1_LEN 6
+#define S2_LEN 4
+
+struct has_strings {
+ char a;
+ char s1[S1_LEN];
+ char s2[S2_LEN];
+};
+
+int main(int argc,char*argv[]) {
+ tpl_node *tn;
+ struct has_strings hs;
+
+ strncpy(hs.s1, "draco",S1_LEN);
+ strncpy(hs.s2, "po",S2_LEN);
+
+ tn = tpl_map("c#c#", hs.s1, S1_LEN, hs.s2, S2_LEN);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test57.tpl");
+ tpl_free(tn);
+
+ return(0);
+}
--- /dev/null
+hs.s1 length: 5
+hs.s1: draco
+hs.s2 length: 2
+hs.s2: po
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define S1_LEN 6
+#define S2_LEN 4
+
+struct has_strings {
+ char a;
+ char s1[S1_LEN];
+ char s2[S2_LEN];
+};
+
+int main(int argc,char*argv[]) {
+ tpl_node *tn;
+ struct has_strings hs;
+
+ tn = tpl_map("c#c#", hs.s1, S1_LEN, hs.s2, S2_LEN);
+ tpl_load(tn,TPL_FILE,"/tmp/test57.tpl");
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ printf("hs.s1 length: %d\n", (int)strlen(hs.s1));
+ printf("hs.s1: %s\n", hs.s1);
+ printf("hs.s2 length: %d\n", (int)strlen(hs.s2));
+ printf("hs.s2: %s\n", hs.s2);
+
+ return(0);
+}
--- /dev/null
+hs.s1 length: 5
+hs.s1: draco
+hs.s2 length: 2
+hs.s2: po
--- /dev/null
+hs2.a: t
+hs2.s1 length: 5
+hs2.s1: draco
+hs2.b: h
+hs2.s2 length: 2
+hs2.s2: po
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define S1_LEN 6
+#define S2_LEN 4
+
+struct has_strings {
+ char a;
+ char s1[S1_LEN];
+ char b;
+ char s2[S2_LEN];
+};
+
+int main(int argc,char*argv[]) {
+ tpl_node *tn;
+ struct has_strings hs,hs2;
+ void *img;
+ size_t sz;
+
+ strncpy(hs.s1, "draco",S1_LEN);
+ strncpy(hs.s2, "po",S2_LEN);
+ hs.a = 't';
+ hs.b = 'h';
+
+ tn = tpl_map("cc#cc#", &hs.a, hs.s1, S1_LEN, &hs.b, hs.s2, S2_LEN);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_MEM,&img,&sz);
+ tpl_free(tn);
+
+ /* unpack */
+
+ tn = tpl_map("cc#cc#", &hs2.a, hs2.s1, S1_LEN, &hs2.b, hs2.s2, S2_LEN);
+ tpl_load(tn,TPL_MEM,img,sz);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ free(img);
+
+ printf("hs2.a: %c\n", hs2.a);
+ printf("hs2.s1 length: %d\n", (int)strlen(hs2.s1));
+ printf("hs2.s1: %s\n", hs2.s1);
+ printf("hs2.b: %c\n", hs2.b);
+ printf("hs2.s2 length: %d\n", (int)strlen(hs2.s2));
+ printf("hs2.s2: %s\n", hs2.s2);
+ return(0);
+}
--- /dev/null
+hs2.a: t
+hs2.s1 length: 5
+hs2.s1: draco
+hs2.b: h
+hs2.s2 length: 2
+hs2.s2: po
--- /dev/null
+t is hello, world!
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ char *s,*t;
+ void *addr;
+ int sz;
+
+ tn = tpl_map("s",&s);
+ s = "hello, world!";
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_MEM,&addr,&sz);
+ tpl_free(tn);
+
+ tn = tpl_map("s",&t);
+ tpl_load(tn,TPL_MEM,addr,sz);
+ tpl_unpack(tn,0);
+ printf("t is %s\n", t);
+ free(t);
+ tpl_free(tn);
+ free(addr);
+ return(0);
+}
--- /dev/null
+t is hello, world!
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map("A(i)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"/tmp/test60.tpl");
+
+ /* test load-after-pack: implicit free via tpl_free_keep_map() */
+ tpl_load(tn,TPL_FILE,"/tmp/test60.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
+i is 10
+i is 11
+i is 12
+i is 13
+i is 14
+i is 15
+i is 16
+i is 17
+i is 18
+i is 19
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test61_0.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+
+ /* test load-then-load: implicit free via tpl_free_keep_map */
+ tpl_load(tn, TPL_FILE,"test61_1.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
+i is 10
+i is 11
+i is 12
+i is 13
+i is 14
+i is 15
+i is 16
+i is 17
+i is 18
+i is 19
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
+i is 0
+i is -1
+i is -2
+i is -3
+i is -4
+i is -5
+i is -6
+i is -7
+i is -8
+i is -9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test62_0.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+
+ /* test load-then-pack (then load): implicit free via tpl_free_keep_map */
+ for(i=0;i>-10;i--) tpl_pack(tn,1);
+ tpl_dump(tn, TPL_FILE,"/tmp/test62_1.tpl");
+ tpl_load(tn,TPL_FILE,"/tmp/test62_1.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
+i is 0
+i is -1
+i is -2
+i is -3
+i is -4
+i is -5
+i is -6
+i is -7
+i is -8
+i is -9
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
+i is 0
+i is -1
+i is -2
+i is -3
+i is -4
+i is -5
+i is -6
+i is -7
+i is -8
+i is -9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map("A(i)",&i);
+ for(i=0;i<10;i++) tpl_pack(tn,1);
+
+ /* test pack-then-unpack without dump/load; implicit dump/load*/
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+
+ /* implicit conversion back to output tpl (discards previous data in tpl */
+ for(i=0;i>-10;i--) tpl_pack(tn,1);
+
+ /* one more implicit conversion */
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
+i is 0
+i is -1
+i is -2
+i is -3
+i is -4
+i is -5
+i is -6
+i is -7
+i is -8
+i is -9
--- /dev/null
+s is beta
+sh.str is delta
+bin.addr is zeta, size 5
--- /dev/null
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "tpl.h"
+
+struct str_holder {
+ char str[10];
+};
+
+
+/* the real purpose of this test is to be run under dbx "check -all"
+ * mode to ensure the level-0 data are freed when replaced (i.e. the
+ * level 0 nodes are packed more than once, this is an edge case but
+ * the required behavior is to free the previously-packed data when
+ * packing the new replacement data). Do the test for s,S,and B types.
+ */
+int main() {
+ tpl_node *tn;
+ char *s;
+ struct str_holder sh;
+ tpl_bin bin;
+
+ /* test a replacement pack (s type) of the level 0 node */
+ tn = tpl_map("s", &s);
+ s = "alpha";
+ tpl_pack(tn,0); /* copies alpha */
+ s = "beta";
+ tpl_pack(tn,0); /* should free alpha, copy beta */
+ tpl_dump(tn,TPL_FILE,"/tmp/test64_0.tpl");
+ tpl_free(tn);
+
+ /* print out dumped tpl */
+ s = "";
+ tn = tpl_map("s", &s);
+ tpl_load(tn,TPL_FILE,"/tmp/test64_0.tpl");
+ tpl_unpack(tn,0);
+ printf("s is %s\n", s);
+ free(s);
+ tpl_free(tn);
+
+ /* test replacement pack (S type) of the level 0 node */
+ tn = tpl_map("c#", sh.str, 10);
+ strncpy(sh.str, "gamma", 10);
+ tpl_pack(tn,0); /* copies gamma */
+ strncpy(sh.str, "delta", 10);
+ tpl_pack(tn,0); /* should free gamma, copy delta */
+ tpl_dump(tn,TPL_FILE,"/tmp/test64_1.tpl");
+ tpl_free(tn);
+
+ /* print out dumped tpl */
+ sh.str[0] = '\0';
+ tn = tpl_map("c#", sh.str, 10);
+ tpl_load(tn,TPL_FILE,"/tmp/test64_1.tpl");
+ tpl_unpack(tn,0);
+ printf("sh.str is %s\n", sh.str);
+ tpl_free(tn);
+
+ /* test replacement pack (B type) of the level 0 node */
+ tn = tpl_map("B", &bin);
+ bin.addr = "epsilon";
+ bin.sz = strlen("epsilon")+1;
+ tpl_pack(tn,0); /* copies epsilon */
+ bin.addr = "zeta";
+ bin.sz = strlen("zeta")+1;
+ tpl_pack(tn,0); /* should free epsilon, copy zeta */
+ tpl_dump(tn,TPL_FILE,"/tmp/test64_2.tpl");
+ tpl_free(tn);
+
+ /* print out dumped tpl */
+ bin.addr = "";
+ bin.sz = 1;
+ tn = tpl_map("B", &bin);
+ tpl_load(tn,TPL_FILE,"/tmp/test64_2.tpl");
+ tpl_unpack(tn,0);
+ printf("bin.addr is %s, size %d\n", (char*)(bin.addr), bin.sz);
+ free(bin.addr);
+ tpl_free(tn);
+ return(0);
+
+}
--- /dev/null
+s is beta
+sh.str is delta
+bin.addr is zeta, size 5
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define TEST_LEN 10
+
+int main() {
+ tpl_node *tn;
+ int i[TEST_LEN] = {1,2,3,4,5,6,7,8,9,10};
+
+ tn = tpl_map("i#", i, TEST_LEN);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test65.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define TEST_LEN1 10
+#define TEST_LEN2 5
+
+int main() {
+ tpl_node *tn;
+ int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10};
+ int j[TEST_LEN2] = {5,4,3,2,1};
+
+ tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test66.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+load succeeded
+/tmp/test67.tpl: array lengths mismatch
+load failed
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define TEST_LEN1 10
+#define TEST_LEN2 5
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10};
+ int j[TEST_LEN2] = {5,4,3,2,1};
+
+ tpl_hook.oops = printf; /* errors to printf */
+
+ tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test67.tpl");
+ tpl_free(tn);
+
+ /* Expect success on the next line */
+ tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2);
+ if (tpl_load(tn,TPL_FILE,"/tmp/test67.tpl") < 0) {
+ printf("load failed\n");
+ } else {
+ printf("load succeeded\n");
+ }
+ tpl_free(tn);
+
+ /* Expect failure on the next line (TEST_LEN2 != TEST_LEN2-1) */
+ tn = tpl_map("i#i#", i, TEST_LEN1, j, (TEST_LEN2 - 1));
+ if (tpl_load(tn,TPL_FILE,"/tmp/test67.tpl") < 0) {
+ printf("load failed\n");
+ } else {
+ printf("load succeeded\n");
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+load succeeded
+/tmp/test67.tpl: array lengths mismatch
+load failed
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define TEST_LEN1 10
+#define TEST_LEN2 5
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10};
+ int j[TEST_LEN2] = {5,4,3,2,1};
+ int i2[TEST_LEN1];
+ int j2[TEST_LEN2];
+ int x;
+
+ tpl_hook.oops = printf; /* errors to printf */
+
+ tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test68.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("i#i#", i2, TEST_LEN1, j2, TEST_LEN2);
+ tpl_load(tn,TPL_FILE,"/tmp/test68.tpl");
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ for(x=0;x<TEST_LEN1;x++) {
+ if (i[x] != i2[x]) printf("mismatch (i)!\n");
+ }
+ for(x=0;x<TEST_LEN2;x++) {
+ if (j[x] != j2[x]) printf("mismatch (j)!\n");
+ }
+ return(0);
+}
--- /dev/null
+2 3 4 5 6 7 8 9 10 11
+4 3 2 1 0
+3 4 5 6 7 8 9 10 11 12
+3 2 1 0 -1
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define TEST_LEN1 10
+#define TEST_LEN2 5
+
+extern tpl_hook_t tpl_hook;
+
+int main() {
+ tpl_node *tn;
+ int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10};
+ int j[TEST_LEN2] = {5,4,3,2,1};
+ int i2[TEST_LEN1];
+ int j2[TEST_LEN2];
+ int x,y;
+
+ tpl_hook.oops = printf; /* errors to printf */
+
+ tn = tpl_map("A(i#i#)", i, TEST_LEN1, j, TEST_LEN2);
+ for(y=0; y < 2; y++) {
+ for(x=0; x < TEST_LEN1; x++) i[x] += 1;
+ for(x=0; x < TEST_LEN2; x++) j[x] -= 1;
+ tpl_pack(tn,1);
+ }
+ tpl_dump(tn,TPL_FILE,"/tmp/test69.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(i#i#)", i2, TEST_LEN1, j2, TEST_LEN2);
+ tpl_load(tn,TPL_FILE,"/tmp/test69.tpl");
+ while (tpl_unpack(tn,1) > 0) {
+ for(x=0; x < TEST_LEN1; x++) printf("%d ", i2[x]);
+ printf("\n");
+ for(x=0; x < TEST_LEN2; x++) printf("%d ", j2[x]);
+ printf("\n");
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+2 3 4 5 6 7 8 9 10 11
+4 3 2 1 0
+3 4 5 6 7 8 9 10 11 12
+3 2 1 0 -1
--- /dev/null
+t is wonderful
+t is prince of peace
+t is counselor
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ char *strs[] = { "wonderful", "prince of peace", "counselor", NULL };
+ int i;
+ char *s, *t;
+
+ tn = tpl_map("A(s)",&s);
+ for(i=0; strs[i] != NULL; i++) {
+ s = strs[i];
+ tpl_pack(tn,1);
+ }
+ tpl_dump(tn,TPL_FILE,"/tmp/test7.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(s)",&t);
+ tpl_load(tn,TPL_FILE,"/tmp/test7.tpl");
+ while (tpl_unpack(tn,1) > 0) {
+ printf("t is %s\n", t);
+ free(t);
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+t is wonderful
+t is prince of peace
+t is counselor
--- /dev/null
+a
+1
+hi
+o
+matilda
+y
+3.140000
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tpl.h"
+
+struct ms_t {
+ char c;
+ int i;
+ char s[2];
+ char o;
+ char *x;
+ char y;
+ double d;
+};
+
+int main() {
+ tpl_node *tn;
+ struct ms_t ms = {/*.c =*/ 'a', /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ 'y', /*.d =*/ 3.14 };
+ struct ms_t ms2;
+
+ tn = tpl_map("S(cic#cscf)", &ms, 2);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test70.tpl");
+ tpl_free(tn);
+
+ memset(&ms2,0,sizeof(struct ms_t));
+ tn = tpl_map("S(cic#cscf)", &ms2, 2);
+ tpl_load(tn,TPL_FILE,"/tmp/test70.tpl");
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ printf("%c\n%d\n%c%c\n%c\n%s\n%c\n%f\n", ms2.c, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y, ms2.d);
+ free(ms2.x);
+ return(0);
+}
--- /dev/null
+a
+1
+hi
+o
+matilda
+y
+3.140000
--- /dev/null
+#
+1
+hi
+o
+matilda
+y
+3.140000
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tpl.h"
+
+struct ms_t {
+ int i;
+ char s[2];
+ char o;
+ char *x;
+ char y;
+ double d;
+};
+
+int main() {
+ tpl_node *tn;
+ struct ms_t ms = { /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ 'y', /*.d =*/ 3.14 };
+ struct ms_t ms2;
+ char b = '#', b2;
+
+ tn = tpl_map("cS(ic#cscf)", &b, &ms, 2);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test71.tpl");
+ tpl_free(tn);
+
+ memset(&ms2,0,sizeof(struct ms_t));
+ tn = tpl_map("cS(ic#cscf)", &b2, &ms2, 2);
+ tpl_load(tn,TPL_FILE,"/tmp/test71.tpl");
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ printf("%c\n%d\n%c%c\n%c\n%s\n%c\n%f\n", b2, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y, ms2.d);
+ free(ms2.x);
+ return(0);
+}
--- /dev/null
+#
+1
+hi
+o
+matilda
+y
+3.140000
--- /dev/null
+a
+1
+hi
+o
+matilda
+y
+3.140000
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tpl.h"
+
+struct ms_t {
+ char c;
+ int i;
+ char s[2];
+ char o;
+ char *x;
+ char y;
+ double d;
+};
+
+int main() {
+ tpl_node *tn;
+ struct ms_t ms = {/*.c =*/ 'a', /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ 'y', /*.d =*/ 3.14 };
+ struct ms_t ms2;
+
+ tn = tpl_map("S(cic#cscf)", &ms, 2);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test72.tpl");
+ tpl_free(tn);
+
+ memset(&ms2,0,sizeof(struct ms_t));
+ tn = tpl_map("S(cic#cscf)", &ms2, 2);
+ tpl_load(tn,TPL_FILE,"/tmp/test72.tpl");
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ printf("%c\n%d\n%c%c\n%c\n%s\n%c\n%f\n", ms2.c, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y, ms2.d);
+ free(ms2.x);
+ return(0);
+}
--- /dev/null
+a
+1
+hi
+o
+matilda
+y
+3.140000
--- /dev/null
+a
+1
+hi
+o
+matilda
+tdh
+3.140000
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tpl.h"
+
+struct ms_t {
+ char c;
+ int i;
+ char s[2];
+ char o;
+ char *x;
+ char y[3];
+ double d;
+};
+
+int main() {
+ tpl_node *tn;
+ struct ms_t ms = {/*.c =*/ 'a', /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ {'t','d','h'}, /*.d =*/ 3.14 };
+ struct ms_t ms2;
+
+ tn = tpl_map("S(cic#csc#f)", &ms, 2, 3);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test73.tpl");
+ tpl_free(tn);
+
+ memset(&ms2,0,sizeof(struct ms_t));
+ tn = tpl_map("S(cic#csc#f)", &ms2, 2, 3);
+ tpl_load(tn,TPL_FILE,"/tmp/test73.tpl");
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ printf("%c\n%d\n%c%c\n%c\n%s\n%c%c%c\n%f\n", ms2.c, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y[0],ms2.y[1],ms2.y[2], ms2.d);
+ free(ms2.x);
+ return(0);
+}
--- /dev/null
+a
+1
+hi
+o
+matilda
+tdh
+3.140000
--- /dev/null
+1234
+0.00
+1.50
+3.00
+4.50
+6.00
+7.50
+9.00
+10.50
+12.00
+13.50
+15.00
+16.50
+18.00
+19.50
+21.00
+22.50
+24.00
+25.50
+27.00
+28.50
+30.00
+31.50
+33.00
+34.50
+36.00
+37.50
+39.00
+40.50
+42.00
+43.50
+45.00
+46.50
+48.00
+49.50
+51.00
+52.50
+54.00
+55.50
+57.00
+58.50
+60.00
+61.50
+63.00
+64.50
+66.00
+67.50
+69.00
+70.50
+72.00
+73.50
+75.00
+76.50
+78.00
+79.50
+81.00
+82.50
+84.00
+85.50
+87.00
+88.50
+90.00
+91.50
+93.00
+94.50
+96.00
+97.50
+99.00
+100.50
+102.00
+103.50
+105.00
+106.50
+108.00
+109.50
+111.00
+112.50
+114.00
+115.50
+117.00
+118.50
+120.00
+121.50
+123.00
+124.50
+126.00
+127.50
+129.00
+130.50
+132.00
+133.50
+135.00
+136.50
+138.00
+139.50
+141.00
+142.50
+144.00
+145.50
+147.00
+148.50
+150.00
+151.50
+153.00
+154.50
+156.00
+157.50
+159.00
+160.50
+162.00
+163.50
+165.00
+166.50
+168.00
+169.50
+171.00
+172.50
+174.00
+175.50
+177.00
+178.50
+180.00
+181.50
+183.00
+184.50
+186.00
+187.50
+189.00
+190.50
+192.00
+193.50
+195.00
+196.50
+198.00
+199.50
+201.00
+202.50
+204.00
+205.50
+207.00
+208.50
+210.00
+211.50
+213.00
+214.50
+216.00
+217.50
+219.00
+220.50
+222.00
+223.50
+225.00
+226.50
+228.00
+229.50
+231.00
+232.50
+234.00
+235.50
+237.00
+238.50
+240.00
+241.50
+243.00
+244.50
+246.00
+247.50
+249.00
+250.50
+252.00
+253.50
+255.00
+256.50
+258.00
+259.50
+261.00
+262.50
+264.00
+265.50
+267.00
+268.50
+270.00
+271.50
+273.00
+274.50
+276.00
+277.50
+279.00
+280.50
+282.00
+283.50
+285.00
+286.50
+288.00
+289.50
+291.00
+292.50
+294.00
+295.50
+297.00
+298.50
+0.00
+0.50
+1.00
+1.50
+2.00
+2.50
+3.00
+3.50
+4.00
+4.50
+5.00
+5.50
+6.00
+6.50
+7.00
+7.50
+8.00
+8.50
+9.00
+9.50
+10.00
+10.50
+11.00
+11.50
+12.00
+12.50
+13.00
+13.50
+14.00
+14.50
+15.00
+15.50
+16.00
+16.50
+17.00
+17.50
+18.00
+18.50
+19.00
+19.50
+20.00
+20.50
+21.00
+21.50
+22.00
+22.50
+23.00
+23.50
+24.00
+24.50
+25.00
+25.50
+26.00
+26.50
+27.00
+27.50
+28.00
+28.50
+29.00
+29.50
+30.00
+30.50
+31.00
+31.50
+32.00
+32.50
+33.00
+33.50
+34.00
+34.50
+35.00
+35.50
+36.00
+36.50
+37.00
+37.50
+38.00
+38.50
+39.00
+39.50
+40.00
+40.50
+41.00
+41.50
+42.00
+42.50
+43.00
+43.50
+44.00
+44.50
+45.00
+45.50
+46.00
+46.50
+47.00
+47.50
+48.00
+48.50
+49.00
+49.50
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "tpl.h"
+
+#define F1_LEN 200
+#define F2_LEN 100
+
+struct ms_t {
+ int i;
+ double f1[F1_LEN];
+ double f2[F2_LEN];
+};
+
+int main() {
+ tpl_node *tn;
+ struct ms_t ms, ms2;
+ int i;
+
+ ms.i = 1234;
+ for(i=0; i < F1_LEN; i++) ms.f1[i] = i * 1.5;
+ for(i=0; i < F2_LEN; i++) ms.f2[i] = i * 0.5;
+
+ tn = tpl_map("S(if#f#)", &ms, F1_LEN, F2_LEN);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,"/tmp/test74.tpl");
+ tpl_free(tn);
+
+ memset(&ms2,0,sizeof(struct ms_t));
+ tn = tpl_map("S(if#f#)", &ms2, F1_LEN, F2_LEN);
+ tpl_load(tn,TPL_FILE,"/tmp/test74.tpl");
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ printf("%d\n", ms2.i);
+ for(i=0; i < F1_LEN; i++) printf("%.2f\n", ms2.f1[i]);
+ for(i=0; i < F2_LEN; i++) printf("%.2f\n", ms2.f2[i]);
+
+ return(0);
+}
--- /dev/null
+1234
+0.00
+1.50
+3.00
+4.50
+6.00
+7.50
+9.00
+10.50
+12.00
+13.50
+15.00
+16.50
+18.00
+19.50
+21.00
+22.50
+24.00
+25.50
+27.00
+28.50
+30.00
+31.50
+33.00
+34.50
+36.00
+37.50
+39.00
+40.50
+42.00
+43.50
+45.00
+46.50
+48.00
+49.50
+51.00
+52.50
+54.00
+55.50
+57.00
+58.50
+60.00
+61.50
+63.00
+64.50
+66.00
+67.50
+69.00
+70.50
+72.00
+73.50
+75.00
+76.50
+78.00
+79.50
+81.00
+82.50
+84.00
+85.50
+87.00
+88.50
+90.00
+91.50
+93.00
+94.50
+96.00
+97.50
+99.00
+100.50
+102.00
+103.50
+105.00
+106.50
+108.00
+109.50
+111.00
+112.50
+114.00
+115.50
+117.00
+118.50
+120.00
+121.50
+123.00
+124.50
+126.00
+127.50
+129.00
+130.50
+132.00
+133.50
+135.00
+136.50
+138.00
+139.50
+141.00
+142.50
+144.00
+145.50
+147.00
+148.50
+150.00
+151.50
+153.00
+154.50
+156.00
+157.50
+159.00
+160.50
+162.00
+163.50
+165.00
+166.50
+168.00
+169.50
+171.00
+172.50
+174.00
+175.50
+177.00
+178.50
+180.00
+181.50
+183.00
+184.50
+186.00
+187.50
+189.00
+190.50
+192.00
+193.50
+195.00
+196.50
+198.00
+199.50
+201.00
+202.50
+204.00
+205.50
+207.00
+208.50
+210.00
+211.50
+213.00
+214.50
+216.00
+217.50
+219.00
+220.50
+222.00
+223.50
+225.00
+226.50
+228.00
+229.50
+231.00
+232.50
+234.00
+235.50
+237.00
+238.50
+240.00
+241.50
+243.00
+244.50
+246.00
+247.50
+249.00
+250.50
+252.00
+253.50
+255.00
+256.50
+258.00
+259.50
+261.00
+262.50
+264.00
+265.50
+267.00
+268.50
+270.00
+271.50
+273.00
+274.50
+276.00
+277.50
+279.00
+280.50
+282.00
+283.50
+285.00
+286.50
+288.00
+289.50
+291.00
+292.50
+294.00
+295.50
+297.00
+298.50
+0.00
+0.50
+1.00
+1.50
+2.00
+2.50
+3.00
+3.50
+4.00
+4.50
+5.00
+5.50
+6.00
+6.50
+7.00
+7.50
+8.00
+8.50
+9.00
+9.50
+10.00
+10.50
+11.00
+11.50
+12.00
+12.50
+13.00
+13.50
+14.00
+14.50
+15.00
+15.50
+16.00
+16.50
+17.00
+17.50
+18.00
+18.50
+19.00
+19.50
+20.00
+20.50
+21.00
+21.50
+22.00
+22.50
+23.00
+23.50
+24.00
+24.50
+25.00
+25.50
+26.00
+26.50
+27.00
+27.50
+28.00
+28.50
+29.00
+29.50
+30.00
+30.50
+31.00
+31.50
+32.00
+32.50
+33.00
+33.50
+34.00
+34.50
+35.00
+35.50
+36.00
+36.50
+37.00
+37.50
+38.00
+38.50
+39.00
+39.50
+40.00
+40.50
+41.00
+41.50
+42.00
+42.50
+43.00
+43.50
+44.00
+44.50
+45.00
+45.50
+46.00
+46.50
+47.00
+47.50
+48.00
+48.50
+49.00
+49.50
--- /dev/null
+string
+byte array
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define S2_LEN 10
+
+struct example {
+ char *s1; /* s1 is a pointer */
+ char s2[S2_LEN]; /* s2 is a byte array */
+};
+
+int main() {
+ tpl_node *tn;
+ int i;
+ struct example dst, src = {
+ /* .s1 = */ "string",
+ /* .s2 = */ {'b','y','t','e',' ','a','r','r','a','y'}
+ };
+
+ tn = tpl_map( "sc#", &src.s1, &src.s2, S2_LEN);
+ tpl_pack( tn, 0 );
+ tpl_dump( tn, TPL_FILE, "/tmp/test75.tpl" );
+ tpl_free( tn );
+
+ /* unpack it now into another struct */
+
+ tn = tpl_map( "sc#", &dst.s1, &dst.s2, S2_LEN);
+ tpl_load( tn, TPL_FILE, "/tmp/test75.tpl" );
+ tpl_unpack( tn, 0 );
+ tpl_free( tn );
+
+ printf("%s\n", dst.s1);
+ for(i=0; i < S2_LEN; i++) printf("%c", dst.s2[i]);
+ printf("\n");
+
+ free(dst.s1); /* tpl allocated it for us; we must free it */
+ return(0);
+}
--- /dev/null
+string
+byte array
--- /dev/null
+string
+byte array
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define S2_LEN 10
+
+struct example {
+ char *s1; /* s1 is a pointer */
+ char s2[S2_LEN]; /* s2 is a byte array */
+};
+
+int main() {
+ tpl_node *tn;
+ int i;
+ struct example dst, src = {
+ /* .s1 = */ "string",
+ /* .s2 = */ {'b','y','t','e',' ','a','r','r','a','y'}
+ };
+
+ tn = tpl_map( "S(sc#)", &src, S2_LEN); /* NOTE S(...) */
+ tpl_pack( tn, 0 );
+ tpl_dump( tn, TPL_FILE, "/tmp/test76.tpl" );
+ tpl_free( tn );
+
+ /* unpack it now into another struct */
+
+ tn = tpl_map( "S(sc#)", &dst, S2_LEN); /* NOTE S(...) */
+ tpl_load( tn, TPL_FILE, "/tmp/test76.tpl" );
+ tpl_unpack( tn, 0 );
+ tpl_free( tn );
+
+ printf("%s\n", dst.s1);
+ for(i=0; i < S2_LEN; i++) printf("%c", dst.s2[i]);
+ printf("\n");
+
+ free(dst.s1); /* tpl allocated it for us; we must free it */
+ return(0);
+}
--- /dev/null
+string
+byte array
--- /dev/null
+string
+byte array
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+#define S2_LEN 10
+
+struct example {
+ char *s1; /* s1 is a pointer */
+ char s2[S2_LEN]; /* s2 is a byte array */
+};
+
+int main() {
+ tpl_node *tn;
+ int i;
+ struct example dst, src = {
+ /* .s1 = */ "string",
+ /* .s2 = */ {'b','y','t','e',' ','a','r','r','a','y'}
+ };
+
+ tn = tpl_map( "S(sc#)", &src, S2_LEN); /* NOTE S(...) */
+ tpl_pack( tn, 0 );
+ tpl_dump( tn, TPL_FILE, "/tmp/test77.tpl" );
+ tpl_free( tn );
+
+ /* unpack it now into another struct */
+
+ tn = tpl_map( "S(sc#)", &dst, S2_LEN);
+ tpl_load( tn, TPL_FILE, "/tmp/test77.tpl" );
+ tpl_unpack( tn, 0 );
+ tpl_free( tn );
+
+ printf("%s\n", dst.s1);
+ for(i=0; i < S2_LEN; i++) printf("%c", dst.s2[i]);
+ printf("\n");
+
+ free(dst.s1); /* tpl allocated it for us; we must free it */
+ return(0);
+}
--- /dev/null
+string
+byte array
--- /dev/null
+#include "tpl.h"
+
+int main() {
+ int i;
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(i)c", &i, &c);
+
+ /* pack index number 0 (char c) */
+ c = 'a';
+ tpl_pack(tn, 0);
+
+ /* pack A(i) (that is, index number 1) a few times */
+ i = 3;
+ tpl_pack(tn, 1);
+ i = 4;
+ tpl_pack(tn, 1);
+
+ tpl_dump(tn, TPL_FILE, "/tmp/test78.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+got a
+got 3
+got 4
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ int i;
+ char c;
+ tpl_node *tn;
+
+ tn = tpl_map("A(i)c", &i, &c);
+ tpl_load(tn, TPL_FILE, "/tmp/test78.tpl");
+
+ /* unpack index number 0 (char c) */
+ tpl_unpack(tn, 0);
+ printf("got %c\n", c);
+
+ /* unpack A(i) (that is, index number 1) til we run out of elements */
+ while (tpl_unpack(tn, 1) > 0) {
+ printf("got %d\n", i);
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+got a
+got 3
+got 4
--- /dev/null
+d is ff, e is 61
+d is ff, e is 62
+d is ff, e is 63
+d is ff, e is 64
+d is ff, e is 65
+d is ff, e is 66
+d is ff, e is 67
+d is ff, e is 68
+d is ff, e is 69
+d is ff, e is 6a
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ unsigned int i;
+ unsigned char b,c;
+ unsigned char d,e;
+
+ tn = tpl_map("cA(c)",&b,&c);
+ b = 255;
+ tpl_pack(tn,0);
+ for (i=0; i < 10; i++) {
+ c = 'a' + i;
+ tpl_pack(tn,1);
+ }
+ tpl_dump(tn,TPL_FILE,"/tmp/test8.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("cA(c)",&d,&e);
+ tpl_load(tn,TPL_FILE,"/tmp/test8.tpl");
+ tpl_unpack(tn,0);
+ while (tpl_unpack(tn,1) > 0)
+ printf("d is %x, e is %x\n", (unsigned)d, (unsigned)e);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+d is ff, e is 61
+d is ff, e is 62
+d is ff, e is 63
+d is ff, e is 64
+d is ff, e is 65
+d is ff, e is 66
+d is ff, e is 67
+d is ff, e is 68
+d is ff, e is 69
+d is ff, e is 6a
--- /dev/null
+a 0 1 2 3 4 5 6 7 8 9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+#define ILEN 10
+
+struct st {
+ char c;
+ int i[ILEN];
+};
+
+int main() {
+ struct st dst, s = {'a', {0,1,2,3,4,5,6,7,8,9}};
+ tpl_node *tn;
+ int j;
+
+ tn = tpl_map("A(S(ci#))", &s, ILEN);
+ tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,"/tmp/test80.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(S(ci#))", &dst, ILEN);
+ tpl_load(tn,TPL_FILE,"/tmp/test80.tpl");
+ while (tpl_unpack(tn,1) > 0) {
+ printf("%c ", dst.c);
+ for(j=0;j<ILEN;j++) printf("%d ", dst.i[j]);
+ printf("\n");
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+a 0 1 2 3 4 5 6 7 8 9
--- /dev/null
+a 0 1 2 3 4 5 6 7 8 9
+b 1 2 3 4 5 6 7 8 9 10
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+#define ILEN 10
+
+struct st {
+ char c;
+ int i[ILEN];
+};
+
+int main() {
+ struct st dst, s = {'a', {0,1,2,3,4,5,6,7,8,9}};
+ tpl_node *tn;
+ int j;
+
+ tn = tpl_map("A(S(ci#))", &s, ILEN);
+ tpl_pack(tn,1);
+
+ /* fiddle with the fields and pack another element */
+ s.c++;
+ for(j=0;j<ILEN;j++) s.i[j]++;
+ tpl_pack(tn,1);
+
+ tpl_dump(tn,TPL_FILE,"/tmp/test81.tpl");
+ tpl_free(tn);
+
+ tn = tpl_map("A(S(ci#))", &dst, ILEN);
+ tpl_load(tn,TPL_FILE,"/tmp/test81.tpl");
+ while (tpl_unpack(tn,1) > 0) {
+ printf("%c ", dst.c);
+ for(j=0;j<ILEN;j++) printf("%d ", dst.i[j]);
+ printf("\n");
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+a 0 1 2 3 4 5 6 7 8 9
+b 1 2 3 4 5 6 7 8 9 10
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+#define ILEN 10
+#define KLEN 8
+#define FLEN 5
+
+struct st {
+ char c;
+ double f[FLEN];
+};
+
+int main() {
+ tpl_node *tn;
+ /* some meaningless test data */
+ struct st s = {'z', {0.9, 0.8, 0.7, 0.6, 0.5 }};
+ int j;
+ int i[ILEN] = {-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
+ int k[KLEN] = {100, 200, 300, 400, 500, 600, 700, 800};
+ char a = '&';
+ char b = 'x';
+
+ tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN);
+ tpl_pack(tn,0);
+
+ tpl_pack(tn,1);
+ for(j=0; j < ILEN; j++) i[j]--;
+ tpl_pack(tn,1);
+ for(j=0; j < ILEN; j++) i[j]--;
+ tpl_pack(tn,1);
+
+ tpl_pack(tn,2);
+ b++;
+ for(j=0; j < KLEN; j++) k[j] += 50;
+ tpl_pack(tn,2);
+ b++;
+ for(j=0; j < KLEN; j++) k[j] += 50;
+ tpl_pack(tn,2);
+
+ tpl_dump(tn,TPL_FILE,"/tmp/test82.tpl");
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+& z 0.90 0.80 0.70 0.60 0.50
+-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
+-2 -3 -4 -5 -6 -7 -8 -9 -10 -11
+-3 -4 -5 -6 -7 -8 -9 -10 -11 -12
+x 100 200 300 400 500 600 700 800
+y 150 250 350 450 550 650 750 850
+z 200 300 400 500 600 700 800 900
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+#define ILEN 10
+#define KLEN 8
+#define FLEN 5
+
+struct st {
+ char c;
+ double f[FLEN];
+};
+
+int main() {
+ tpl_node *tn;
+ /* some meaningless test data */
+ struct st s;
+ int j;
+ int i[ILEN];
+ int k[KLEN];
+ char a;
+ char b;
+
+ tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN);
+ tpl_load(tn,TPL_FILE,"/tmp/test82.tpl");
+
+ tpl_unpack(tn,0);
+ printf("%c %c %.2f %.2f %.2f %.2f %.2f \n", a, s.c, s.f[0], s.f[1], s.f[2], s.f[3], s.f[4]);
+
+ while( tpl_unpack(tn,1) > 0) {
+ for(j=0;j<ILEN;j++) printf("%d ", i[j]);
+ printf("\n");
+ }
+
+ while( tpl_unpack(tn,2) > 0) {
+ printf("%c ", b);
+ for(j=0;j<KLEN;j++) printf("%d ", k[j]);
+ printf("\n");
+ }
+
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+& z 0.90 0.80 0.70 0.60 0.50
+-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
+-2 -3 -4 -5 -6 -7 -8 -9 -10 -11
+-3 -4 -5 -6 -7 -8 -9 -10 -11 -12
+x 100 200 300 400 500 600 700 800
+y 150 250 350 450 550 650 750 850
+z 200 300 400 500 600 700 800 900
--- /dev/null
+& z 0.90 0.80 0.70 0.60 0.50
+-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
+-2 -3 -4 -5 -6 -7 -8 -9 -10 -11
+-3 -4 -5 -6 -7 -8 -9 -10 -11 -12
+x 100 200 300 400 500 600 700 800
+y 150 250 350 450 550 650 750 850
+z 200 300 400 500 600 700 800 900
+& z 0.90 0.80 0.70 0.60 0.50
+-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
+-2 -3 -4 -5 -6 -7 -8 -9 -10 -11
+-3 -4 -5 -6 -7 -8 -9 -10 -11 -12
+x 100 200 300 400 500 600 700 800
+y 150 250 350 450 550 650 750 850
+z 200 300 400 500 600 700 800 900
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+#define ILEN 10
+#define KLEN 8
+#define FLEN 5
+
+struct st {
+ char c;
+ double f[FLEN];
+};
+
+int main() {
+ tpl_node *tn;
+ /* some meaningless test data */
+ struct st s;
+ int j;
+ int i[ILEN];
+ int k[KLEN];
+ char a;
+ char b;
+
+ tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN);
+ tpl_load(tn,TPL_FILE,"test84_0.tpl");
+
+ tpl_unpack(tn,0);
+ printf("%c %c %.2f %.2f %.2f %.2f %.2f \n", a, s.c, s.f[0], s.f[1], s.f[2], s.f[3], s.f[4]);
+
+ while( tpl_unpack(tn,1) > 0) {
+ for(j=0;j<ILEN;j++) printf("%d ", i[j]);
+ printf("\n");
+ }
+
+ while( tpl_unpack(tn,2) > 0) {
+ printf("%c ", b);
+ for(j=0;j<KLEN;j++) printf("%d ", k[j]);
+ printf("\n");
+ }
+
+ tpl_free(tn);
+
+ /* use the big-endian input file and repeat */
+
+ tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN);
+ tpl_load(tn,TPL_FILE,"test84_1.tpl");
+
+ tpl_unpack(tn,0);
+ printf("%c %c %.2f %.2f %.2f %.2f %.2f \n", a, s.c, s.f[0], s.f[1], s.f[2], s.f[3], s.f[4]);
+
+ while( tpl_unpack(tn,1) > 0) {
+ for(j=0;j<ILEN;j++) printf("%d ", i[j]);
+ printf("\n");
+ }
+
+ while( tpl_unpack(tn,2) > 0) {
+ printf("%c ", b);
+ for(j=0;j<KLEN;j++) printf("%d ", k[j]);
+ printf("\n");
+ }
+
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+& z 0.90 0.80 0.70 0.60 0.50
+-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
+-2 -3 -4 -5 -6 -7 -8 -9 -10 -11
+-3 -4 -5 -6 -7 -8 -9 -10 -11 -12
+x 100 200 300 400 500 600 700 800
+y 150 250 350 450 550 650 750 850
+z 200 300 400 500 600 700 800 900
+& z 0.90 0.80 0.70 0.60 0.50
+-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
+-2 -3 -4 -5 -6 -7 -8 -9 -10 -11
+-3 -4 -5 -6 -7 -8 -9 -10 -11 -12
+x 100 200 300 400 500 600 700 800
+y 150 250 350 450 550 650 750 850
+z 200 300 400 500 600 700 800 900
--- /dev/null
+cA(i#)S(cf#)A(ci#)
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ char *fmt;
+ fmt = tpl_peek(TPL_FILE, "test85.tpl");
+ if (fmt) {
+ printf("%s\n",fmt);
+ free(fmt);
+ }
+ return 0;
+}
--- /dev/null
+cA(i#)S(cf#)A(ci#)
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int o,i;
+ void *addr;
+ int sz;
+ char *fmt;
+
+ tn = tpl_map("A(A(i))",&i);
+ for(o=0;o<10;o++) {
+ for(i=o; i < o+10; i++) tpl_pack(tn,2);
+ tpl_pack(tn,1);
+ }
+ tpl_dump(tn,TPL_MEM,&addr,&sz);
+ tpl_free(tn);
+
+ fmt = tpl_peek(TPL_MEM, addr, sz);
+ if (fmt) {
+ printf("%s\n",fmt);
+ free(fmt);
+ }
+ free(addr);
+ return(0);
+}
--- /dev/null
+sum is 49995000
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main() {
+ tpl_node *tn;
+ unsigned i, sum=0;
+ int fd[2], pid,rc;
+ void *img;
+ size_t sz;
+
+ pipe(fd);
+ if ( (pid = fork()) == 0) { /* child */
+
+ rc = tpl_gather(TPL_GATHER_BLOCKING,fd[0],&img,&sz);
+ if (rc != 1) {
+ printf("error: rc non-zero: %d\n", rc);
+ exit(-1);
+ }
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_MEM, img,sz);
+ while (tpl_unpack(tn,1) > 0) sum += i;
+ tpl_free(tn);
+ printf("sum is %d\n", sum);
+
+ } else if (pid > 0) { /* parent */
+
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10000;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FD, fd[1] );
+ tpl_free(tn);
+
+ close(fd[1]);
+ waitpid(pid,NULL,0);
+
+ } else if (pid == -1) {
+ perror("fork error");
+ }
+ return(0);
+}
--- /dev/null
+sum is 49995000
--- /dev/null
+#include "tpl.h"
+
+struct ms_t {
+ int i;
+ char c[3];
+ double f;
+};
+
+int main() {
+ tpl_node *tn;
+ struct ms_t ms = {1, {'a','b','c'}, 3.14};
+
+ tn = tpl_map( "S(ic#f)", &ms, 3);
+ tpl_pack( tn, 0 );
+ tpl_dump( tn, TPL_FILE, "/tmp/test88.tpl" );
+ tpl_free( tn );
+ return(0);
+}
--- /dev/null
+1
+abc
+3.14
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+struct ms_t {
+ int i;
+ char c[3];
+ double f;
+};
+
+int main() {
+ tpl_node *tn;
+ struct ms_t ms;
+
+ tn = tpl_map( "S(ic#f)", &ms, 3);
+ tpl_load( tn, TPL_FILE, "/tmp/test88.tpl" );
+ tpl_unpack( tn, 0 );
+ tpl_free( tn );
+
+ printf("%d\n%c%c%c\n%.2f\n", ms.i, ms.c[0],ms.c[1],ms.c[2], ms.f);
+ return(0);
+}
--- /dev/null
+1
+abc
+3.14
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+#include <stdio.h>
+#include "tpl.h"
+
+int main() {
+ tpl_node *tn;
+ int i;
+
+ tn = tpl_map("A(i)",&i);
+ tpl_load(tn,TPL_FILE,"test9.tpl");
+ while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i);
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+i is 0
+i is 1
+i is 2
+i is 3
+i is 4
+i is 5
+i is 6
+i is 7
+i is 8
+i is 9
--- /dev/null
+cn is equal to cn2
--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+
+#include <tpl.h>
+
+int main ( int n , char* a [ ] )
+{
+
+ tpl_node* tn ;
+ int64_t cn = 100,cn2 ;
+
+ tn = tpl_map ( "I" , &cn ) ;
+ tpl_pack ( tn , 0 ) ;
+ tpl_dump ( tn , TPL_FILE , "/tmp/test90.tpl" ) ;
+ tpl_free ( tn ) ;
+
+ tn = tpl_map ( "I" , &cn2 ) ;
+ tpl_load ( tn , TPL_FILE , "/tmp/test90.tpl" ) ;
+ tpl_unpack ( tn , 0 ) ;
+ printf("cn is %sequal to cn2\n", (cn == cn2) ? "" : "not");
+ tpl_free ( tn ) ;
+ return ( 0 ) ;
+}
--- /dev/null
+cn is equal to cn2
--- /dev/null
+cn is equal to cn2
--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+
+#include <tpl.h>
+
+int main ( int n , char* a [ ] )
+{
+
+ tpl_node* tn ;
+ uint64_t cn = 100, cn2 ;
+
+ tn = tpl_map ( "U" , &cn ) ;
+ tpl_pack ( tn , 0 ) ;
+ tpl_dump ( tn , TPL_FILE , "/tmp/test91.tpl" ) ;
+ tpl_free ( tn ) ;
+
+ tn = tpl_map ( "U" , &cn2 ) ;
+ tpl_load ( tn , TPL_FILE , "/tmp/test91.tpl" ) ;
+ tpl_unpack ( tn , 0 ) ;
+ printf("cn is %sequal to cn2\n", (cn == cn2) ? "" : "not");
+ tpl_free ( tn ) ;
+ return ( 0 ) ;
+}
--- /dev/null
+cn is equal to cn2
--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+
+#include <tpl.h>
+
+int main ( int n , char* a [ ] )
+{
+
+ tpl_node* tn ;
+ char c='a',c2='b',c3,c4;
+ int64_t cn = -100, cn2 ;
+ uint64_t ucn = 200, ucn2;
+
+ tn = tpl_map ( "A(cIcU)" , &c, &cn, &c2, &ucn ) ;
+ tpl_pack ( tn , 1 ) ;
+ c += 1;
+ cn -= 1;
+ c2 += 1;
+ ucn += 1;
+ tpl_pack ( tn , 1 ) ;
+ tpl_dump ( tn , TPL_FILE , "/tmp/test92.tpl" ) ;
+ tpl_free ( tn ) ;
+
+ tn = tpl_map ( "A(cIcU)" , &c3, &cn2, &c4, &ucn2 ) ;
+ tpl_load(tn,TPL_FILE,"/tmp/test92.tpl");
+ /* Hesitant to rely on portability of %lld to print int64_t.
+ At least on MinGW it is questionable. */
+ /*
+ * while (tpl_unpack(tn,1) > 0) {
+ * printf("%c %lld %c %llu\n", c3, cn2, c4, ucn2);
+ * }
+ */
+ tpl_unpack(tn,1);
+ if (c3 != 'a' || cn2 != -100 || c4 != 'b' || ucn2 != 200) printf("unpack error 1\n");
+ tpl_unpack(tn,1);
+ if (c3 != 'b' || cn2 != -101 || c4 != 'c' || ucn2 != 201) printf("unpack error 2\n");
+ return ( 0 ) ;
+
+}
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename="/tmp/test93.tpl";
+int main() {
+ tpl_node *tn;
+ char *s = NULL;
+ tn = tpl_map("s", &s);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ s = (char*)0x1; /* overwritten below */
+ tn = tpl_map("s", &s);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ printf("s %s null\n", (s==NULL?"is":"is NOT"));
+ return(0);
+}
+
--- /dev/null
+s is hello
+s is NULL
+s is hello
+s is NULL
+s is hello
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename="/tmp/test94.tpl";
+int main() {
+ tpl_node *tn;
+ int i;
+ char *s = NULL;
+ tn = tpl_map("A(s)", &s);
+ for(i=0;i<5;i++) {
+ s = (i&1) ? NULL : "hello"; /* odd i are NULL string */
+ tpl_pack(tn,1);
+ }
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ s = (char*)0x1; /* overwritten below */
+ tn = tpl_map("A(s)", &s);
+ tpl_load(tn,TPL_FILE,filename);
+ while( tpl_unpack(tn,1) > 0) {
+ printf("s is %s\n", (s?s:"NULL"));
+ }
+ tpl_free(tn);
+ return(0);
+}
+
--- /dev/null
+s is hello
+s is NULL
+s is hello
+s is NULL
+s is hello
--- /dev/null
+s1 NULL
+s2
+s3 hello
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename="/tmp/test95.tpl";
+int main() {
+ tpl_node *tn;
+ char *s1 = NULL, *s2 = "", *s3 = "hello";
+ tn = tpl_map("sss", &s1, &s2, &s3);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ s1 = s2 = s3 = (char*)0x1; /* overwritten below */
+ tn = tpl_map("sss", &s1, &s2, &s3);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+ printf("s1 %s\n", s1?s1:"NULL");
+ printf("s2 %s\n", s2?s2:"NULL");
+ printf("s3 %s\n", s3?s3:"NULL");
+ return(0);
+}
+
--- /dev/null
+s1 NULL
+s2
+s3 hello
--- /dev/null
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+
+const char *filename="/tmp/test96.tpl";
+int main() {
+ tpl_node *tn;
+ int i;
+ char *s1 = NULL, *s2 = "", *s3 = "hello";
+ tn = tpl_map("A(sss)", &s1, &s2, &s3);
+ for(i=0;i<5;i++) {
+ tpl_pack(tn,1);
+ }
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ s1 = s2 = s3 = (char*)0x1; /* overwritten below */
+ tn = tpl_map("A(sss)", &s1, &s2, &s3);
+ tpl_load(tn,TPL_FILE,filename);
+ while( tpl_unpack(tn,1) > 0) {
+ printf("s1 %s\n", s1?s1:"NULL");
+ printf("s2 %s\n", s2?s2:"NULL");
+ printf("s3 %s\n", s3?s3:"NULL");
+ }
+ tpl_free(tn);
+ return(0);
+}
+
--- /dev/null
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
+s1 NULL
+s2
+s3 hello
--- /dev/null
+j is -128, v is 65535
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <inttypes.h>
+
+const char *filename = "/tmp/test97.tpl";
+
+int main() {
+ tpl_node *tn;
+ int16_t j = -128;
+ uint16_t v=65535;
+
+ tn = tpl_map("jv", &j, &v);
+ tpl_pack(tn,0);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ j = v = 0;
+
+ tn = tpl_map("jv", &j, &v);
+ tpl_load(tn,TPL_FILE,filename);
+ tpl_unpack(tn,0);
+ tpl_free(tn);
+
+ printf("j is %d, v is %d\n", (int)j, (int)v);
+ return(0);
+
+}
--- /dev/null
+j is -128, v is 65535
--- /dev/null
+j is -128, v is 65535
+j is -129, v is 65534
+j is -130, v is 65533
--- /dev/null
+#include "tpl.h"
+#include <stdio.h>
+#include <inttypes.h>
+
+const char *filename = "/tmp/test98.tpl";
+
+int main() {
+ tpl_node *tn;
+ int16_t j = -128;
+ uint16_t v=65535;
+
+ tn = tpl_map("A(jv)", &j, &v);
+ tpl_pack(tn,1); j -= 1; v-= 1;
+ tpl_pack(tn,1); j -= 1; v-= 1;
+ tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FILE,filename);
+ tpl_free(tn);
+
+ j = v = 0;
+
+ tn = tpl_map("A(jv)", &j, &v);
+ tpl_load(tn,TPL_FILE,filename);
+ while (tpl_unpack(tn,1) > 0) {
+ printf("j is %d, v is %d\n", (int)j, (int)v);
+ }
+ tpl_free(tn);
+ return(0);
+}
--- /dev/null
+j is -128, v is 65535
+j is -129, v is 65534
+j is -130, v is 65533
--- /dev/null
+cA(i#)S(cf#)A(ci#)
+&
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+
+int main() {
+ char *fmt, c;
+ fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, "test99.tpl", "c", &c);
+ if (fmt) {
+ printf("%s\n",fmt);
+ free(fmt);
+ printf("%c\n", c);
+ }
+ return 0;
+}
--- /dev/null
+cA(i#)S(cf#)A(ci#)
+&
--- /dev/null
+TPLSRC = ../../src
+PROGS = test1
+
+# Thread support requires compiler-specific options
+# ----------------------------------------------------------------------------
+# GNU
+CFLAGS = -I$(TPLSRC) -g -pthread
+# Solaris (Studio 11 on Sparc Ultra3)
+#CFLAGS = -I$(TPLSRC) -g -mt
+# ----------------------------------------------------------------------------
+
+all: $(PROGS) run_tests
+
+tpl.o : $(TPLSRC)/tpl.c $(TPLSRC)/tpl.h
+ $(CC) -c $(CFLAGS) $(TPLSRC)/tpl.c
+
+$(PROGS) : tpl.o
+ $(CC) $(CFLAGS) -o $@ $(@).c tpl.o
+
+run_tests: $(PROGS)
+ perl ../do_tests
+
+.PHONY: clean
+
+clean:
+ rm -f $(PROGS) tpl.o test*.out
--- /dev/null
+Tests for using tpl in threaded programs.
+
+thread_ipc.c a thread version of the pipe IPC test ../test28.c
--- /dev/null
+sum is 49995000
+thread result: 0 null
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tpl.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+int fd[2];
+
+void *thread_routine( void *arg ) {
+ tpl_node *tn;
+ int i,sum=0;
+
+ /* child */
+ tn = tpl_map("A(u)",&i);
+ tpl_load(tn, TPL_FD, fd[0]);
+ while (tpl_unpack(tn,1) > 0) sum += i;
+ tpl_free(tn);
+ printf("sum is %d\n", sum);
+ return NULL;
+}
+
+int main() {
+ tpl_node *tn;
+ unsigned i;
+ int status;
+ pthread_t thread_id;
+ void *thread_result;
+
+ pipe(fd);
+ if ( status = pthread_create( &thread_id, NULL, thread_routine, NULL )) {
+ printf("failure: status %d\n", status);
+ exit(-1);
+ }
+ /* parent */
+ tn = tpl_map("A(u)",&i);
+ for(i=0;i<10000;i++) tpl_pack(tn,1);
+ tpl_dump(tn,TPL_FD, fd[1] );
+ tpl_free(tn);
+
+ status = pthread_join( thread_id, &thread_result );
+ printf("thread result: %d %s\n", status, thread_result ? "non-null":"null");
+}
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;"\r
+ AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;libs\libtpl-1.5\src;libs\libtpl-1.5\src\win;"\r
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;FREESWITCHCORE_EXPORTS;PCRE_STATIC;STATICLIB"\r
MinimalRebuild="true"\r
BasicRuntimeChecks="3"\r
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;"\r
+ AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;libs\libtpl-1.5\src;libs\libtpl-1.5\src\win;"\r
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;FREESWITCHCORE_EXPORTS;PCRE_STATIC;STATICLIB"\r
MinimalRebuild="true"\r
BasicRuntimeChecks="3"\r
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;"\r
+ AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;libs\libtpl-1.5\src;libs\libtpl-1.5\src\win;"\r
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;FREESWITCHCORE_EXPORTS;STATICLIB;CRASH_PROT;PCRE_STATIC"\r
RuntimeLibrary="2"\r
UsePrecompiledHeader="2"\r
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;"\r
+ AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;..\..\libs\spandsp\src\msvc;..\..\libs\spandsp\src;..\..\libs\tiff-4.0.2\libtiff;libs\libtpl-1.5\src;libs\libtpl-1.5\src\win;"\r
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;FREESWITCHCORE_EXPORTS;STATICLIB;CRASH_PROT;PCRE_STATIC"\r
RuntimeLibrary="2"\r
UsePrecompiledHeader="2"\r
/>\r
</FileConfiguration>\r
</File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\tpl.c"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|x64"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|x64"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\win\mmap.c"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|x64"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|x64"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
<File\r
RelativePath="..\..\src\switch_apr.c"\r
>\r
RelativePath="..\..\libs\stfu\stfu.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\tpl.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\win\mman.h"\r
+ >\r
+ </File>\r
<File\r
RelativePath="..\..\src\include\switch.h"\r
>\r
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include"\r
+ AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;libs\libtpl-1.5\src;libs\libtpl-1.5\src\win;"\r
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;FREESWITCHCORE_EXPORTS;PCRE_STATIC;STATICLIB"\r
MinimalRebuild="true"\r
BasicRuntimeChecks="3"\r
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include"\r
+ AdditionalIncludeDirectories="..\..\src\include;..\..\libs\include;..\..\libs\srtp\include;..\..\libs\srtp\crypto\include;..\..\libs\libteletone\src;..\..\libs\win32\sqlite;..\..\libs\pcre;..\..\libs\stfu;..\..\libs\speex\include;libs\libtpl-1.5\src;libs\libtpl-1.5\src\win;"\r
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;FREESWITCHCORE_EXPORTS;STATICLIB;CRASH_PROT;PCRE_STATIC"\r
RuntimeLibrary="2"\r
UsePrecompiledHeader="2"\r
/>\r
</FileConfiguration>\r
</File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\tpl.c"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\win\mmap.c"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ UsePrecompiledHeader="0"\r
+ PrecompiledHeaderThrough=""\r
+ PrecompiledHeaderFile=""\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
<File\r
RelativePath="..\..\src\switch_apr.c"\r
>\r
RelativePath="..\..\libs\stfu\stfu.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\tpl.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\libs\libtpl-1.5\src\win\mman.h"\r
+ >\r
+ </File>\r
<File\r
RelativePath="..\..\src\include\switch.h"\r
>\r