summaryrefslogtreecommitdiff
path: root/agen5
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2012-08-11 16:45:31 +0000
committerLorry <lorry@roadtrain.codethink.co.uk>2012-10-18 12:43:06 +0000
commit7c432b265ed7ca5f8304938db73912df8ce35032 (patch)
treec95de7bee7b742ed92b9924d904adca706e48d80 /agen5
downloadautogen-master.tar.gz
Imported from /srv/lorry/lorry-area/autogen/autogen-5.16.2.tar.gz.HEADautogen-5.16.2master
Diffstat (limited to 'agen5')
-rw-r--r--agen5/Makefile.am174
-rw-r--r--agen5/Makefile.in1013
-rw-r--r--agen5/ag-text.c736
-rw-r--r--agen5/ag-text.h856
-rw-r--r--agen5/agCgi.c172
-rw-r--r--agen5/agDep.c447
-rw-r--r--agen5/agInit.c684
-rw-r--r--agen5/agShell.c695
-rw-r--r--agen5/agUtils.c564
-rw-r--r--agen5/autogen.1559
-rw-r--r--agen5/autogen.c657
-rw-r--r--agen5/autogen.h570
-rw-r--r--agen5/bootstrap.dir148
-rw-r--r--agen5/cgi-fsm.c323
-rw-r--r--agen5/cgi-fsm.h91
-rw-r--r--agen5/cgi.def55
-rw-r--r--agen5/defDirect.c1048
-rw-r--r--agen5/defFind.c944
-rw-r--r--agen5/defLex.c678
-rw-r--r--agen5/defLoad.c486
-rw-r--r--agen5/defParse-fsm.c389
-rw-r--r--agen5/defParse-fsm.h92
-rw-r--r--agen5/defParse.def109
-rw-r--r--agen5/defParse.x245
-rw-r--r--agen5/directive.h125
-rw-r--r--agen5/directive.tpl141
-rw-r--r--agen5/expExtract.c360
-rw-r--r--agen5/expFormat.c874
-rw-r--r--agen5/expGperf.c156
-rw-r--r--agen5/expGuile.c522
-rw-r--r--agen5/expMake.c429
-rw-r--r--agen5/expOutput.c934
-rw-r--r--agen5/expPrint.c344
-rw-r--r--agen5/expState.c819
-rw-r--r--agen5/expString.c1132
-rw-r--r--agen5/expr.h160
-rw-r--r--agen5/expr.ini270
-rw-r--r--agen5/fmemopen.c743
-rw-r--r--agen5/fsm-macro.tlib402
-rw-r--r--agen5/fsm-trans.tlib359
-rw-r--r--agen5/fsm.tpl207
-rw-r--r--agen5/funcCase.c1390
-rw-r--r--agen5/funcDef.c899
-rw-r--r--agen5/funcEval.c651
-rw-r--r--agen5/funcFor.c918
-rw-r--r--agen5/funcIf.c514
-rw-r--r--agen5/functions.c539
-rw-r--r--agen5/functions.h325
-rw-r--r--agen5/guile-iface.def183
-rw-r--r--agen5/guile-iface.h95
-rw-r--r--agen5/guile-iface.tpl116
-rw-r--r--agen5/invoke-autogen.texi872
-rw-r--r--agen5/loadPseudo.c497
-rw-r--r--agen5/mk-stamps.sh298
-rw-r--r--agen5/opts.c1491
-rw-r--r--agen5/opts.def998
-rw-r--r--agen5/opts.h305
-rw-r--r--agen5/proto.h300
-rw-r--r--agen5/pseudo-fsm.h227
-rw-r--r--agen5/pseudo.def69
-rw-r--r--agen5/schemedef.scm388
-rw-r--r--agen5/scmStrings.c203
-rw-r--r--agen5/snarf.tpl316
-rw-r--r--agen5/test/Makefile.am71
-rw-r--r--agen5/test/Makefile.in581
-rwxr-xr-xagen5/test/alist.test79
-rwxr-xr-xagen5/test/case.test136
-rwxr-xr-xagen5/test/columns.test56
-rwxr-xr-xagen5/test/daemon.test127
-rwxr-xr-xagen5/test/debug.test175
-rwxr-xr-xagen5/test/define.test160
-rwxr-xr-xagen5/test/defref.test124
-rwxr-xr-xagen5/test/directives.test148
-rwxr-xr-xagen5/test/dynref.test82
-rwxr-xr-xagen5/test/endmac.test73
-rwxr-xr-xagen5/test/error.test250
-rwxr-xr-xagen5/test/expr.test107
-rwxr-xr-xagen5/test/extract.test80
-rwxr-xr-xagen5/test/for.test112
-rwxr-xr-xagen5/test/forfrom.test79
-rwxr-xr-xagen5/test/forin.test86
-rwxr-xr-xagen5/test/format.test96
-rwxr-xr-xagen5/test/get.test95
-rwxr-xr-xagen5/test/gperf.test95
-rwxr-xr-xagen5/test/heredef.test102
-rwxr-xr-xagen5/test/html.test130
-rwxr-xr-xagen5/test/in.test95
-rwxr-xr-xagen5/test/include.test80
-rwxr-xr-xagen5/test/leave.test105
-rwxr-xr-xagen5/test/license.test114
-rwxr-xr-xagen5/test/line.test68
-rwxr-xr-xagen5/test/loop.test78
-rwxr-xr-xagen5/test/make.test91
-rwxr-xr-xagen5/test/match.test72
-rwxr-xr-xagen5/test/opts.test91
-rwxr-xr-xagen5/test/output.test120
-rwxr-xr-xagen5/test/pseudo.test103
-rwxr-xr-xagen5/test/reorder.test107
-rwxr-xr-xagen5/test/shell.test165
-rwxr-xr-xagen5/test/snarf.test267
-rwxr-xr-xagen5/test/stack.test93
-rwxr-xr-xagen5/test/stress.test68
-rwxr-xr-xagen5/test/string.test247
-rwxr-xr-xagen5/test/strtable.test97
-rwxr-xr-xagen5/test/strxform.test87
-rwxr-xr-xagen5/test/suffix.test90
-rwxr-xr-xagen5/test/time.test64
-rw-r--r--agen5/tpLoad.c560
-rw-r--r--agen5/tpParse.c400
-rw-r--r--agen5/tpProcess.c413
110 files changed, 38025 insertions, 0 deletions
diff --git a/agen5/Makefile.am b/agen5/Makefile.am
new file mode 100644
index 0000000..4adc1da
--- /dev/null
+++ b/agen5/Makefile.am
@@ -0,0 +1,174 @@
+## -*- Mode: Makefile -*-
+##
+## Makefile.am -- process this file with automake to produce Makefile.in
+##
+## Time-stamp: "2012-04-01 05:21:26 bkorb"
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+SUBDIRS = test
+INCLUDES = @INCLIST@
+FSMTPL = fsm.tpl fsm-macro.tlib fsm-trans.tlib
+TEMPLATES = directive.tpl snarf.tpl $(FSMTPL)
+DEFINITIONS = cgi.def opts.def pseudo.def defParse.def
+GEN_CSRC = opts.h directive.h expr.ini expr.h proto.h pseudo-fsm.h
+pkgdata_DATA = $(FSMTPL)
+man_MANS = autogen.1
+DISTCLEANFILES = stamp-* ag.c
+bin_PROGRAMS = autogen
+
+# The list of source files with AutoGen functions
+#
+FUNCLIST = \
+ funcCase.c funcDef.c funcEval.c funcFor.c funcIf.c functions.c
+
+# The list of source files with Guile expression functions defined
+#
+EXPRLIST = \
+ expExtract.c expFormat.c expGperf.c expGuile.c expMake.c \
+ expOutput.c expPrint.c expState.c expString.c agShell.c \
+ $(FUNCLIST)
+
+FSMLIST = cgi-fsm.c defParse-fsm.c
+csrc = \
+ autogen.c ag-text.c $(FSMLIST) $(EXPRLIST) agCgi.c \
+ agDep.c agInit.c agUtils.c defDirect.c defFind.c \
+ defLex.c defLoad.c fmemopen.c loadPseudo.c opts.c \
+ scmStrings.c tpLoad.c tpParse.c tpProcess.c
+
+hsrc = ag-text.h autogen.h defParse-fsm.h functions.h cgi-fsm.h guile-iface.h
+
+EXTRA_DIST = $(csrc) $(hsrc) $(DEFINITIONS) $(TEMPLATES) $(man_MANS) \
+ defParse.x bootstrap.dir schemedef.scm \
+ guile-iface.def guile-iface.tpl \
+ invoke-autogen.texi mk-stamps.sh
+
+LO_LIB = $(top_builddir)/autoopts/libopts.la
+SNV_LIB = $(top_builddir)/snprintfv/libsnprintfv.la
+
+nodist_autogen_SOURCES = ag.c
+autogen_SOURCES = $(GEN_CSRC)
+autogen_LDADD = $(LO_LIB) $(SNV_LIB) $(LIBGUILE_LIBS)
+autogen_LDFLAGS = $(DYNAMIC_AG)
+autogen_CFLAGS = $(LIBGUILE_CFLAGS)
+
+AM_YFLAGS = -d
+
+ag.c : Makefile
+ exec > $@ ; \
+ echo '#undef PKGDATADIR' ; \
+ echo '#define PKGDATADIR "$(pkgdatadir)"' ; \
+ mk=`set -- $(MAKE) ; which $$1` ; \
+ echo 'static char const zMakeProg[] = "'$$mk'";' ; \
+ echo ; echo '#define DEFINING' ; \
+ echo '#include "autoopts/project.h"' ; \
+ echo '#include "autoopts/ag-char-map.h"' ; \
+ echo '#include "autogen.h"' ; \
+ for f in $(csrc) ; do echo "#include \"$$f\"" ; done
+
+STAMPENV = top_srcdir=$(top_srcdir) top_builddir=$(top_builddir) \
+ srcdir=$(srcdir) AGexe=$(AGexe) GDexe=$(GDexe) CLexe=$(CLexe) \
+ DEPDIR="$(DEPDIR)"
+
+MAKE_STAMP = $(STAMPENV) $(POSIX_SHELL) $(srcdir)/bootstrap.dir $@
+
+../snprintfv/snprintfv.h :
+ @if [ ! -f ../snprintfv/snprintfv.h ] ; then cd ../snprintfv ; \
+ ln -s $(top_srcdir)/snprintfv/snprintfv/snprintfv.h . ; fi
+
+../snprintfv/libsnprintfv.la :
+ cd ../snprintfv ; $(MAKE) libsnprintfv.la
+
+.NOTPARALLEL:
+
+# start-generated-text
+
+list_stamps = \
+ stamp-opts stamp-proto stamp-parse stamp-cgi \
+ stamp-pseudo stamp-exprini stamp-directive stamp-texi \
+ stamp-ag_text stamp-fmem stamp-man stamp-func \
+ stamp-gver
+if AMDEP
+@am__include@ @am__quote@./$(DEPDIR)/stamp-opts.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-proto.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-parse.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-cgi.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-pseudo.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-exprini.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-directive.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-texi.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-ag_text.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-fmem.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-man.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-func.d@am__quote@
+@am__include@ @am__quote@./$(DEPDIR)/stamp-gver.d@am__quote@
+endif
+
+stamp-opts:
+ @target="$(AUTOGEN_stamp_opts_TList)" \
+ $(MAKE_STAMP)
+
+stamp-proto:
+ @target="$(AUTOGEN_stamp_proto_TList)" \
+ $(MAKE_STAMP)
+
+stamp-parse:
+ @target="$(AUTOGEN_stamp_parse_TList)" \
+ $(MAKE_STAMP)
+
+stamp-cgi:
+ @target="$(AUTOGEN_stamp_cgi_TList)" \
+ $(MAKE_STAMP)
+
+stamp-pseudo:
+ @target="$(AUTOGEN_stamp_pseudo_TList)" \
+ $(MAKE_STAMP)
+
+stamp-exprini:
+ @target="$(AUTOGEN_stamp_exprini_TList)" \
+ $(MAKE_STAMP)
+
+stamp-directive:
+ @target="$(AUTOGEN_stamp_directive_TList)" \
+ $(MAKE_STAMP)
+
+stamp-texi:
+ @target="$(AUTOGEN_stamp_texi_TList)" \
+ $(MAKE_STAMP)
+
+stamp-ag_text:
+ @target="$(AUTOGEN_stamp_ag_text_TList)" \
+ $(MAKE_STAMP)
+
+stamp-fmem:
+ @target="$(AUTOGEN_stamp_fmem_TList)" \
+ $(MAKE_STAMP)
+
+stamp-man:
+ @target="$(AUTOGEN_stamp_man_TList)" \
+ $(MAKE_STAMP)
+
+stamp-func:
+ @target="$(AUTOGEN_stamp_func_TList)" \
+ $(MAKE_STAMP)
+
+stamp-gver:
+ @target="$(AUTOGEN_stamp_gver_TList)" \
+ $(MAKE_STAMP)
+
+# end-generated-text
+# end of Makefile.am
diff --git a/agen5/Makefile.in b/agen5/Makefile.in
new file mode 100644
index 0000000..9070217
--- /dev/null
+++ b/agen5/Makefile.in
@@ -0,0 +1,1013 @@
+# Makefile.in generated by automake 1.12.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2012 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@
+am__make_dryrun = \
+ { \
+ am__dry=no; \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
+ | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
+ *) \
+ for am__flg in $$MAKEFLAGS; do \
+ case $$am__flg in \
+ *=*|--*) ;; \
+ *n*) am__dry=yes; break;; \
+ esac; \
+ done;; \
+ esac; \
+ test $$am__dry = yes; \
+ }
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+bin_PROGRAMS = autogen$(EXEEXT)
+subdir = agen5
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(top_srcdir)/config/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/config/ag_macros.m4 \
+ $(top_srcdir)/config/extensions.m4 \
+ $(top_srcdir)/config/libopts.m4 \
+ $(top_srcdir)/config/libtool.m4 \
+ $(top_srcdir)/config/ltoptions.m4 \
+ $(top_srcdir)/config/ltsugar.m4 \
+ $(top_srcdir)/config/ltversion.m4 \
+ $(top_srcdir)/config/lt~obsolete.m4 \
+ $(top_srcdir)/config/onceonly.m4 \
+ $(top_srcdir)/config/snprintfv.m4 \
+ $(top_srcdir)/config/unlocked-io.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \
+ "$(DESTDIR)$(pkgdatadir)"
+PROGRAMS = $(bin_PROGRAMS)
+am__objects_1 =
+am_autogen_OBJECTS = $(am__objects_1)
+nodist_autogen_OBJECTS = autogen-ag.$(OBJEXT)
+autogen_OBJECTS = $(am_autogen_OBJECTS) $(nodist_autogen_OBJECTS)
+am__DEPENDENCIES_1 =
+autogen_DEPENDENCIES = $(LO_LIB) $(SNV_LIB) $(am__DEPENDENCIES_1)
+autogen_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(autogen_CFLAGS) $(CFLAGS) \
+ $(autogen_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(autogen_SOURCES) $(nodist_autogen_SOURCES)
+DIST_SOURCES = $(autogen_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
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man1dir = $(mandir)/man1
+NROFF = nroff
+MANS = $(man_MANS)
+DATA = $(pkgdata_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AGEN5_TESTS = @AGEN5_TESTS@
+AG_GUILE = @AG_GUILE@
+AG_LDFLAGS = @AG_LDFLAGS@
+AG_MAJOR_VERSION = @AG_MAJOR_VERSION@
+AG_MINOR_VERSION = @AG_MINOR_VERSION@
+AG_TIMEOUT = @AG_TIMEOUT@
+AG_VERSION = @AG_VERSION@
+AG_XML2 = @AG_XML2@
+AGexe = @AGexe@
+AGnam = @AGnam@
+AMTAR = @AMTAR@
+AO_AGE = @AO_AGE@
+AO_CURRENT = @AO_CURRENT@
+AO_REVISION = @AO_REVISION@
+AO_TEMPLATE_VERSION = @AO_TEMPLATE_VERSION@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CLexe = @CLexe@
+CLnam = @CLnam@
+CONFIG_SHELL = @CONFIG_SHELL@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEBUG_ENABLED = @DEBUG_ENABLED@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DYNAMIC_AG = @DYNAMIC_AG@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_STATIC = @ENABLE_STATIC@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GDexe = @GDexe@
+GDnam = @GDnam@
+GO_AGE = @GO_AGE@
+GO_CURRENT = @GO_CURRENT@
+GO_REVISION = @GO_REVISION@
+GREP = @GREP@
+GUILE_VERSION = @GUILE_VERSION@
+INCLIST = @INCLIST@
+INCSNPRINTFV = @INCSNPRINTFV@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBGUILE_CFLAGS = @LIBGUILE_CFLAGS@
+LIBGUILE_LIBS = @LIBGUILE_LIBS@
+LIBGUILE_PATH = @LIBGUILE_PATH@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSNPRINTFV = @LIBSNPRINTFV@
+LIBTOOL = @LIBTOOL@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIBXML2_PATH = @LIBXML2_PATH@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+M4_SRC = @M4_SRC@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPTS_TESTDIR = @OPTS_TESTDIR@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSIX_SHELL = @POSIX_SHELL@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TEXI2HTML = @TEXI2HTML@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_aux_dir = @ac_aux_dir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+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@
+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 = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = test
+INCLUDES = @INCLIST@
+FSMTPL = fsm.tpl fsm-macro.tlib fsm-trans.tlib
+TEMPLATES = directive.tpl snarf.tpl $(FSMTPL)
+DEFINITIONS = cgi.def opts.def pseudo.def defParse.def
+GEN_CSRC = opts.h directive.h expr.ini expr.h proto.h pseudo-fsm.h
+pkgdata_DATA = $(FSMTPL)
+man_MANS = autogen.1
+DISTCLEANFILES = stamp-* ag.c
+
+# The list of source files with AutoGen functions
+#
+FUNCLIST = \
+ funcCase.c funcDef.c funcEval.c funcFor.c funcIf.c functions.c
+
+
+# The list of source files with Guile expression functions defined
+#
+EXPRLIST = \
+ expExtract.c expFormat.c expGperf.c expGuile.c expMake.c \
+ expOutput.c expPrint.c expState.c expString.c agShell.c \
+ $(FUNCLIST)
+
+FSMLIST = cgi-fsm.c defParse-fsm.c
+csrc = \
+ autogen.c ag-text.c $(FSMLIST) $(EXPRLIST) agCgi.c \
+ agDep.c agInit.c agUtils.c defDirect.c defFind.c \
+ defLex.c defLoad.c fmemopen.c loadPseudo.c opts.c \
+ scmStrings.c tpLoad.c tpParse.c tpProcess.c
+
+hsrc = ag-text.h autogen.h defParse-fsm.h functions.h cgi-fsm.h guile-iface.h
+EXTRA_DIST = $(csrc) $(hsrc) $(DEFINITIONS) $(TEMPLATES) $(man_MANS) \
+ defParse.x bootstrap.dir schemedef.scm \
+ guile-iface.def guile-iface.tpl \
+ invoke-autogen.texi mk-stamps.sh
+
+LO_LIB = $(top_builddir)/autoopts/libopts.la
+SNV_LIB = $(top_builddir)/snprintfv/libsnprintfv.la
+nodist_autogen_SOURCES = ag.c
+autogen_SOURCES = $(GEN_CSRC)
+autogen_LDADD = $(LO_LIB) $(SNV_LIB) $(LIBGUILE_LIBS)
+autogen_LDFLAGS = $(DYNAMIC_AG)
+autogen_CFLAGS = $(LIBGUILE_CFLAGS)
+AM_YFLAGS = -d
+STAMPENV = top_srcdir=$(top_srcdir) top_builddir=$(top_builddir) \
+ srcdir=$(srcdir) AGexe=$(AGexe) GDexe=$(GDexe) CLexe=$(CLexe) \
+ DEPDIR="$(DEPDIR)"
+
+MAKE_STAMP = $(STAMPENV) $(POSIX_SHELL) $(srcdir)/bootstrap.dir $@
+
+# start-generated-text
+list_stamps = \
+ stamp-opts stamp-proto stamp-parse stamp-cgi \
+ stamp-pseudo stamp-exprini stamp-directive stamp-texi \
+ stamp-ag_text stamp-fmem stamp-man stamp-func \
+ stamp-gver
+
+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) --gnu agen5/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu agen5/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+autogen$(EXEEXT): $(autogen_OBJECTS) $(autogen_DEPENDENCIES) $(EXTRA_autogen_DEPENDENCIES)
+ @rm -f autogen$(EXEEXT)
+ $(autogen_LINK) $(autogen_OBJECTS) $(autogen_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/autogen-ag.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(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@ $(am__mv) $(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@ $(am__mv) $(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 $@ $<
+
+autogen-ag.o: ag.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(autogen_CFLAGS) $(CFLAGS) -MT autogen-ag.o -MD -MP -MF $(DEPDIR)/autogen-ag.Tpo -c -o autogen-ag.o `test -f 'ag.c' || echo '$(srcdir)/'`ag.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/autogen-ag.Tpo $(DEPDIR)/autogen-ag.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ag.c' object='autogen-ag.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(autogen_CFLAGS) $(CFLAGS) -c -o autogen-ag.o `test -f 'ag.c' || echo '$(srcdir)/'`ag.c
+
+autogen-ag.obj: ag.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(autogen_CFLAGS) $(CFLAGS) -MT autogen-ag.obj -MD -MP -MF $(DEPDIR)/autogen-ag.Tpo -c -o autogen-ag.obj `if test -f 'ag.c'; then $(CYGPATH_W) 'ag.c'; else $(CYGPATH_W) '$(srcdir)/ag.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/autogen-ag.Tpo $(DEPDIR)/autogen-ag.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ag.c' object='autogen-ag.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(autogen_CFLAGS) $(CFLAGS) -c -o autogen-ag.obj `if test -f 'ag.c'; then $(CYGPATH_W) 'ag.c'; else $(CYGPATH_W) '$(srcdir)/ag.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man1: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man1dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.1[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
+install-pkgdataDATA: $(pkgdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgdatadir)" || exit $$?; \
+ done
+
+uninstall-pkgdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir)
+
+# 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) $(RECURSIVE_CLEAN_TARGETS):
+ @fail= 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//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ 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; \
+ ($(am__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"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+cscopelist-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) cscopelist); \
+ 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)
+ set x; \
+ 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 || \
+ set "$$@" "$$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; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ 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)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+cscopelist: cscopelist-recursive $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the 'missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically 'make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS) $(MANS) $(DATA)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(pkgdatadir)"; 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:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+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-binPROGRAMS clean-generic 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
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-man install-pkgdataDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man: install-man1
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+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-binPROGRAMS uninstall-man \
+ uninstall-pkgdataDATA
+
+uninstall-man: uninstall-man1
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) \
+ cscopelist-recursive ctags-recursive install-am install-strip \
+ tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool cscopelist cscopelist-recursive \
+ 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-binPROGRAMS 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-man1 install-pdf install-pdf-am install-pkgdataDATA \
+ 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-binPROGRAMS uninstall-man uninstall-man1 \
+ uninstall-pkgdataDATA
+
+
+ag.c : Makefile
+ exec > $@ ; \
+ echo '#undef PKGDATADIR' ; \
+ echo '#define PKGDATADIR "$(pkgdatadir)"' ; \
+ mk=`set -- $(MAKE) ; which $$1` ; \
+ echo 'static char const zMakeProg[] = "'$$mk'";' ; \
+ echo ; echo '#define DEFINING' ; \
+ echo '#include "autoopts/project.h"' ; \
+ echo '#include "autoopts/ag-char-map.h"' ; \
+ echo '#include "autogen.h"' ; \
+ for f in $(csrc) ; do echo "#include \"$$f\"" ; done
+
+../snprintfv/snprintfv.h :
+ @if [ ! -f ../snprintfv/snprintfv.h ] ; then cd ../snprintfv ; \
+ ln -s $(top_srcdir)/snprintfv/snprintfv/snprintfv.h . ; fi
+
+../snprintfv/libsnprintfv.la :
+ cd ../snprintfv ; $(MAKE) libsnprintfv.la
+
+.NOTPARALLEL:
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-opts.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-proto.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-parse.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-cgi.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-pseudo.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-exprini.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-directive.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-texi.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-ag_text.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-fmem.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-man.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-func.d@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stamp-gver.d@am__quote@
+
+stamp-opts:
+ @target="$(AUTOGEN_stamp_opts_TList)" \
+ $(MAKE_STAMP)
+
+stamp-proto:
+ @target="$(AUTOGEN_stamp_proto_TList)" \
+ $(MAKE_STAMP)
+
+stamp-parse:
+ @target="$(AUTOGEN_stamp_parse_TList)" \
+ $(MAKE_STAMP)
+
+stamp-cgi:
+ @target="$(AUTOGEN_stamp_cgi_TList)" \
+ $(MAKE_STAMP)
+
+stamp-pseudo:
+ @target="$(AUTOGEN_stamp_pseudo_TList)" \
+ $(MAKE_STAMP)
+
+stamp-exprini:
+ @target="$(AUTOGEN_stamp_exprini_TList)" \
+ $(MAKE_STAMP)
+
+stamp-directive:
+ @target="$(AUTOGEN_stamp_directive_TList)" \
+ $(MAKE_STAMP)
+
+stamp-texi:
+ @target="$(AUTOGEN_stamp_texi_TList)" \
+ $(MAKE_STAMP)
+
+stamp-ag_text:
+ @target="$(AUTOGEN_stamp_ag_text_TList)" \
+ $(MAKE_STAMP)
+
+stamp-fmem:
+ @target="$(AUTOGEN_stamp_fmem_TList)" \
+ $(MAKE_STAMP)
+
+stamp-man:
+ @target="$(AUTOGEN_stamp_man_TList)" \
+ $(MAKE_STAMP)
+
+stamp-func:
+ @target="$(AUTOGEN_stamp_func_TList)" \
+ $(MAKE_STAMP)
+
+stamp-gver:
+ @target="$(AUTOGEN_stamp_gver_TList)" \
+ $(MAKE_STAMP)
+
+# end-generated-text
+# end of Makefile.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:
diff --git a/agen5/ag-text.c b/agen5/ag-text.c
new file mode 100644
index 0000000..b157ad4
--- /dev/null
+++ b/agen5/ag-text.c
@@ -0,0 +1,736 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (ag-text.c)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:41 AM by AutoGen 5.16.2pre7
+ * From the definitions /old-home/bkorb/ag/ag/agen5/ag-text.def
+ * and the template file strings
+ *
+ * Copyright (C) 2011-2012 Bruce Korb, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the
+ * Modified (3 clause) Berkeley Software Distribution License
+ * <http://www.xfree86.org/3.3.6/COPYRIGHT2.html>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name ``Bruce Korb'' nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * strings IS PROVIDED BY Bruce Korb ``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 Bruce Korb OR ANY OTHER CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "ag-text.h"
+
+char const ag_text_strtable[15696] =
+/* 0 */ "ag-text.c\0"
+/* 10 */ "%s__\0"
+/* 15 */ "%s=1\0"
+/* 20 */ "ABEND-ing in %s state\n\0"
+/* 43 */ "processing template %s\n"
+ " on line %d\n"
+ " for function %s (%d)\n\0"
+/* 118 */ "ag-fprintf: 'port' is invalid\0"
+/* 148 */ "AUTOGEN_MAKE_DEP\0"
+/* 165 */ "autogen5\0"
+/* 174 */ "AutoGen aborting on signal %d (%s) in state %s\n\0"
+/* 222 */ "Scheme definition expression does not yield string:\n\0"
+/* 275 */ "Scheme Computed Definitions\0"
+/* 303 */ "(alist->autogen-def %s)\0"
+/* 327 */ "could not allocate for or formatting failed on:\n"
+ "%s\0"
+/* 378 */ "asprintfv returned 0x%08X\n\0"
+/* 405 */ "%s Error: Invalid input char '%c' in %s on line %d\n\0"
+/* 457 */ "Cannot %s from outside a loop\0"
+/* 487 */ "Could not resolve macro name: ``%s''\0"
+/* 524 */ "??? indecipherable error message ???\0"
+/* 561 */ "Ill formed name ``%s'' in %s line %d\n\0"
+/* 599 */ "Invalid regular expression: error %d (%s):\n"
+ "%s\0"
+/* 646 */ "** BOGUS **\0"
+/* 658 */ "break\0"
+/* 664 */ "%ld\0"
+/* 668 */ "fserr %d: cannot %s %s: %s\n\0"
+/* 697 */ "duplicate make target\0"
+/* 719 */ "unknown dependency type: %s\0"
+/* 748 */ "Content-type: text/plain\n\n"
+ "AutoGen form processing error:\n\0"
+/* 806 */ "CGI parsing error: %s\0"
+/* 829 */ "/tmp/cgi-stderr-XXXXXX\0"
+/* 852 */ "CLOSING SHELL SERVER - command failure:\n"
+ "\t%s\n\0"
+/* 897 */ "Bad regular expression\0"
+/* 920 */ "continue\0"
+/* 929 */ "\\n\"\n"
+ " \"\0"
+/* 942 */ "%s Error: %s in %s on line %d\n\0"
+/* 974 */ "from %s line %d\0"
+/* 990 */ "DEFINITIONS %s in %s line %d for %s:\n"
+ "\t%s\n\0"
+/* 1032 */ "block\0"
+/* 1038 */ "INVALID\0"
+/* 1046 */ "text\0"
+/* 1051 */ "unknown\0"
+/* 1059 */ " \\\n\0"
+/* 1063 */ "%s_%s\0"
+/* 1069 */ "\n"
+ ".PHONY : clean-%1$s\n\n"
+ "clean-%1$s :\n"
+ "\trm -f %3$s $(%2$s_TList)\n"
+ "\t@-touch -t 199912312359 %1$s\n\0"
+/* 1161 */ "\n"
+ "%2$s : $(%1$s_SList)\n\n"
+ "$(%1$s_TList) : %2$s\n"
+ "\t@:\n\0"
+/* 1210 */ " \\\n"
+ "\t%s\0"
+/* 1217 */ "\n\n"
+ "%s_SList =\0"
+/* 1230 */ "%s_TList =\0"
+/* 1241 */ "DEPENDENCIES_OUTPUT\0"
+/* 1261 */ "/dev/null\0"
+/* 1271 */ "baseless\0"
+/* 1280 */ "-\0"
+/* 1282 */ "=1\0"
+/* 1285 */ "$@\0"
+/* 1288 */ "$$/../share/autogen\0"
+/* 1308 */ "#assert yielded \"%s\":\n"
+ "\t`%s`\0"
+/* 1336 */ "\n"
+ "#\0"
+/* 1339 */ "'#elif' directive encountered out of context\n"
+ "\tin %s on line %d\n\0"
+/* 1403 */ "else\0"
+/* 1408 */ "endif\0"
+/* 1414 */ "endmac\0"
+/* 1421 */ "endshell\0"
+/* 1430 */ "#error directive -- in %s on line %d\n"
+ "\t%s\n\0"
+/* 1472 */ "WARNING: cannot find `%s' definitions file\n\0"
+/* 1517 */ "open\0"
+/* 1522 */ "read\0"
+/* 1527 */ "WARNING %d (%s): cannot stat `%s' for include\n\0"
+/* 1575 */ "def\0"
+/* 1579 */ "WARNING: `%s' must be regular file to include\n\0"
+/* 1627 */ "Definition error: in %s line %d, #endif not found\n\0"
+/* 1679 */ "Definition error: in %s line %d, #%s no matching start/if directive\n\0"
+/* 1749 */ "Computed Definitions\0"
+/* 1770 */ "\n"
+ "#endshell\0"
+/* 1781 */ "Missing #endshell after '#shell' in %s on line %d\n\0"
+/* 1832 */ "done_check done\n\0"
+/* 1849 */ "done_check re-done\n\0"
+/* 1869 */ "%s ERROR: Too many definition files\n\0"
+/* 1907 */ "%s.%s\0"
+/* 1913 */ "\n\n\0"
+/* 1916 */ "%soutput was abandoned\n\0"
+/* 1940 */ "%sBogus return from setjmp\n\0"
+/* 1968 */ "content-type: text/html\n\n\0"
+/* 1994 */ "* NONE *\0"
+/* 2003 */ "Starting stdout template\n\0"
+/* 2029 */ "stdout\0"
+/* 2036 */ "invalid chars in suffix format: %s\0"
+/* 2072 */ "Empty suffix format\0"
+/* 2092 */ "invalid emission port: %d\0"
+/* 2118 */ "*/\0"
+/* 2121 */ "Error in template %s, line %d\n"
+ "\t\0"
+/* 2153 */ "ERROR\0"
+/* 2159 */ "attempted to use block macro in eval expression\0"
+/* 2207 */ "PROGRAM ERROR: ambiguous expr code\0"
+/* 2243 */ "false\0"
+/* 2249 */ "exit_cleanup %s done\n"
+ "AutoGen %u exits\n\0"
+/* 2288 */ "exit_cleanup re-done\n\0"
+/* 2310 */ "no waiting\0"
+/* 2321 */ "(if (> (string-length shell-cleanup) 0) (shellf \"( (%s) & >/dev/null 2>&1 )\" shell-cleanup) )\0"
+/* 2415 */ "waited\0"
+/* 2422 */ "an unknown license\0"
+/* 2441 */ "%6$s%1$sDO NOT EDIT THIS FILE (%2$s)\n"
+ "%1$s\n"
+ "%1$sIt has been AutoGen-ed%3$s\n"
+ "%1$sFrom the definitions %4$s\n"
+ "%1$sand the template file %5$s\0"
+/* 2582 */ "%s -*- buffer-read-only: t -*- vi: set ro:\n"
+ "%s\n\0"
+/* 2629 */ "%6$s%1$sEDIT THIS FILE WITH CAUTION (%2$s)\n"
+ "%1$s\n"
+ "%1$sIt has been AutoGen-ed%3$s\n"
+ "%1$sFrom the definitions %4$s\n"
+ "%1$sand the template file %5$s\0"
+/* 2775 */ "DO NOT CHANGE THIS COMMENT\0"
+/* 2802 */ "END \0"
+/* 2808 */ "START\0"
+/* 2814 */ "# %2$d \"%1$s\"\0"
+/* 2828 */ "name not followed by '='\0"
+/* 2853 */ "no space separating entries\0"
+/* 2881 */ "WARNING: in %s on line %d unknown directive:\n"
+ "\t#%s\n\0"
+/* 2933 */ "failed\n\0"
+/* 2941 */ "SUCCESS\n\0"
+/* 2950 */ ".\0"
+/* 2952 */ "%s/%s\0"
+/* 2958 */ "find-file\0"
+/* 2968 */ "agpl\0"
+/* 2973 */ "read full file\0"
+/* 2988 */ "invalid license file: %s\0"
+/* 3013 */ "lgpl\0"
+/* 3018 */ "mbsd\0"
+/* 3023 */ "stat file\0"
+/* 3033 */ "lic\0"
+/* 3037 */ "WARNING: empty macro in %s line %d\n\0"
+/* 3073 */ "macros cannot nest\0"
+/* 3092 */ "macro has no end\0"
+/* 3109 */ "%d (%s) is an unknown macro function, or has no handler\0"
+/* 3165 */ "? Say, what ?\0"
+/* 3179 */ " -- DEBUG %s -- FOR index %d\0"
+/* 3210 */ " (%c)\0"
+/* 3216 */ "ELSE clause\0"
+/* 3228 */ "FOR x IN ... has no list\0"
+/* 3253 */ "_GUARD\0"
+/* 3260 */ "Failing Guile command: = = = = =\n\n"
+ "%s\n\n"
+ "=================================\n\0"
+/* 3334 */ "GUILE_WARN_DEPRECATED\0"
+/* 3356 */ "GUILE_WARN_DEPRECATED=no\0"
+/* 3381 */ "els\0"
+/* 3385 */ "the\0"
+/* 3389 */ "Invalid template file\0"
+/* 3411 */ "%s=%s\0"
+/* 3417 */ "SHELL\0"
+/* 3423 */ "lse\0"
+/* 3427 */ "ndif\0"
+/* 3432 */ "fdef \0"
+/* 3438 */ "fndef \0"
+/* 3445 */ "HEADER\0"
+/* 3452 */ "Unterminated HereString\0"
+/* 3476 */ "HereString mark 64 or more chars\0"
+/* 3509 */ "HereString missing the mark\0"
+/* 3537 */ "');\n"
+ "document.write('\" >%s</a>');\n"
+ "//-->\n"
+ "</script>\0"
+/* 3586 */ "<script language=\"JavaScript\" type=\"text/javascript\">\n"
+ "<!--\n"
+ "var one = 'm&#97;';\n"
+ "var two = 'i&#108;t';\n"
+ "document.write('<a href=\"' + one + two );\n"
+ "document.write('&#111;:\0"
+/* 3753 */ "&#%d;\0"
+/* 3759 */ "(debug-enable 'backtrace)\0"
+/* 3785 */ "(use-modules (%s)) (read-enable 'positions)\n"
+ "(add-hook! before-error-hook error-source-line)\n"
+ "(use-modules (ice-9 stack-catch))\0"
+/* 3911 */ "system vm trace\0"
+/* 3927 */ "ice-9 debug\0"
+/* 3939 */ "\\n\\\n\0"
+/* 3944 */ "LC_ALL=C\0"
+/* 3953 */ "Unknown macro or invalid context in %s line %d:\n"
+ "\t%s%s\0"
+/* 4007 */ "ESAC not found\0"
+/* 4022 */ "expressionless CASE\0"
+/* 4042 */ "DEFINE requires a name\0"
+/* 4065 */ "parse ended unexpectedly\0"
+/* 4090 */ "Invalid definition name\0"
+/* 4114 */ "`?' needs two expressions\0"
+/* 4140 */ "No space between expressions\0"
+/* 4169 */ "No text for unfound value\0"
+/* 4195 */ "Error %d (%s) reading %d bytes of %s\n\0"
+/* 4233 */ "read failure\0"
+/* 4246 */ "in\0"
+/* 4249 */ "invalid FOR loop variable\0"
+/* 4275 */ "FOR macro requires iterator name\0"
+/* 4308 */ "ENDFOR not found\0"
+/* 4325 */ "ENDIF not found\0"
+/* 4341 */ "The INCLUDE macro requires a file name\0"
+/* 4380 */ "Empty macro text\0"
+/* 4397 */ "Invalid selection clause\0"
+/* 4422 */ "ENDWHILE not found\0"
+/* 4441 */ "expressionless WHILE\0"
+/* 4462 */ "%s\n"
+ "%s\n"
+ "%s\0"
+/* 4471 */ "to port %s with %d type address\0"
+/* 4503 */ "@@ CGI Definitions @@\0"
+/* 4525 */ "GET\0"
+/* 4529 */ "invalid CGI request method: ''%s''\0"
+/* 4564 */ "No CGI data were received\0"
+/* 4590 */ "CGI text\0"
+/* 4599 */ "Template parse ended unexpectedly\0"
+/* 4633 */ "end of Guile/scheme expression not found\0"
+/* 4674 */ "Cannot expand directory name: '%s'\0"
+/* 4710 */ "map data file\0"
+/* 4724 */ "Could not open template '%s'\0"
+/* 4753 */ "not regular file\0"
+/* 4770 */ "agl\0"
+/* 4774 */ "tpl\0"
+/* 4778 */ "\n\n"
+ "* * * * LOG ENTRY %d * * * *\n\0"
+/* 4810 */ "(add-cleanup \"rm -rf ${gpdir}\")\0"
+/* 4842 */ " ; \\\n\0"
+/* 4848 */ "malloc of %zd bytes failed\n\0"
+/* 4876 */ "in-mem file\0"
+/* 4888 */ "mkstemp failed on `%s'\0"
+/* 4911 */ "#ifndef %1$s\n"
+ "#define %1$s 1\0"
+/* 4939 */ "(define header-file \"%s\") (define header-guard \"%s\")\0"
+/* 4992 */ "owner length\0"
+/* 5005 */ "prefix length\0"
+/* 5019 */ "program name length\0"
+/* 5039 */ "license name is not a string\0"
+/* 5068 */ "There is no %s license.\0"
+/* 5092 */ "Could not open license file '%s'\0"
+/* 5125 */ "<owner>\0"
+/* 5133 */ "<PFX>\0"
+/* 5139 */ "<program>\0"
+/* 5149 */ "%s may not exceed %d chars\n\0"
+/* 5177 */ "<years>\0"
+/* 5185 */ "\\%03o\0"
+/* 5191 */ "mk_tmp_dir ; echo ${tmp_dir}/ag-XXXXXX\0"
+/* 5230 */ "no macro arg name\0"
+/* 5248 */ "On macro argument # %d:\n"
+ "%s\n\0"
+/* 5276 */ "\n\0"
+/* 5278 */ "Invalid template file: %s\0"
+/* 5304 */ "expressionless IF\0"
+/* 5322 */ "no\0"
+/* 5325 */ "no template was specified\0"
+/* 5351 */ "ERROR: %s is not a string\n\0"
+/* 5378 */ "NULL file name\0"
+/* 5393 */ "Error %d (%s) opening `%s' for output\0"
+/* 5431 */ "Cannot format file name: \"%s\", %s, %s\0"
+/* 5470 */ "null\0"
+/* 5475 */ "open for output\0"
+/* 5491 */ "unlink\0"
+/* 5498 */ "* temp file *\0"
+/* 5512 */ "No output file specified to add to\0"
+/* 5547 */ "%s '%s'\n\0"
+/* 5556 */ "open 'wb+'\0"
+/* 5567 */ "failed to create temp file from %s\0"
+/* 5602 */ "ERROR: no output file was suspended as ``%s''\n\0"
+/* 5649 */ "ERROR: Cannot pop output with no output pushed\0"
+/* 5697 */ "freopen\0"
+/* 5705 */ "Autogen Definitions cgi;\n\0"
+/* 5731 */ "stdin\0"
+/* 5737 */ "dup2\0"
+/* 5742 */ "%s-in\0"
+/* 5748 */ "%s-out\0"
+/* 5755 */ "fork\0"
+/* 5760 */ "mkfifo\0"
+/* 5767 */ "poll\0"
+/* 5772 */ "write\0"
+/* 5778 */ "(prefix ...) failed\0"
+/* 5798 */ "The INVOKE macro requires a name\0"
+/* 5831 */ "The INVOKE macro name not space separated\0"
+/* 5873 */ "%s%s[%u] (%s) from %s/%d\n\0"
+/* 5899 */ " \0"
+/* 5932 */ "Starting %s template\n\0"
+/* 5954 */ "pseudo-macro\0"
+/* 5967 */ "start marker contained in end marker\0"
+/* 6004 */ "BROKEN FSM STATE\0"
+/* 6021 */ "start/end macro mark too long\0"
+/* 6051 */ "invalid edit mode marker\0"
+/* 6076 */ "need autogen5 marker\0"
+/* 6097 */ "need end marker\0"
+/* 6113 */ "need end of line\0"
+/* 6130 */ "need start marker\0"
+/* 6148 */ "need template marker\0"
+/* 6169 */ "broken pseudo-macro FSM\0"
+/* 6193 */ "end marker contained in start marker\0"
+/* 6230 */ "bad template marker in %s on line %d:\n"
+ "\t%s\0"
+/* 6272 */ "-*-\0"
+/* 6276 */ "*template file*\0"
+/* 6292 */ "@@ No-Definitions @@\0"
+/* 6313 */ "open non-regular file\0"
+/* 6335 */ "stat\0"
+/* 6340 */ "No definition data were read\0"
+/* 6369 */ "realloc of %zd bytes at 0x%p failed\n\0"
+/* 6406 */ "REQUEST_METHOD\0"
+/* 6421 */ "** EXACT **\0"
+/* 6433 */ "** INEXACT **\0"
+/* 6447 */ "** LIST **\0"
+/* 6458 */ "** Pair **\0"
+/* 6469 */ "** Procedure 0x%08lX **\0"
+/* 6493 */ "** UNKNOWN **\0"
+/* 6507 */ "** Vector **\0"
+/* 6520 */ "RETURN out of context\0"
+/* 6542 */ "Bad args to sprintf\0"
+/* 6562 */ "%s ERROR: %s processing printf format:\n"
+ "\t%s\n\0"
+/* 6607 */ "Scheme evaluation error. AutoGen ABEND-ing in template\n"
+ "\t%s on line %d\n\0"
+/* 6679 */ "scm_string_length returned wrong value: %d != %d\n\0"
+/* 6729 */ "\n"
+ "Guile/Scheme evaluation error in %s line %d: %s\n\0"
+/* 6780 */ "#f\0"
+/* 6783 */ "...\0"
+/* 6787 */ "ERROR: Cannot pop output with no output pushed\n\0"
+/* 6836 */ "re-read output\0"
+/* 6851 */ "(*)()\0"
+/* 6857 */ "#t\0"
+/* 6860 */ "cannot allocate path name\0"
+/* 6886 */ "(set! tmp-dir \"%1$s\")\n"
+ "(add-cleanup \"test \\\"${VERBOSE:-false}\\\" = true || rm -rf %1$s\")\0"
+/* 6973 */ "Warning: (set-writable) function in %s on line %d:\n"
+ "\toverridden by invocation option\n\0"
+/* 7058 */ "%s%s\0"
+/* 7063 */ "invalid alist to shellf\0"
+/* 7087 */ "cd %s\n"
+ "%s\n\n"
+ "echo\n"
+ "echo %s - %d\n\0"
+/* 7116 */ "Closing server: %s signal (%d) received\n\0"
+/* 7158 */ "\n"
+ "Last command issued:\n\0"
+/* 7181 */ "feof on data load\n\0"
+/* 7200 */ "fs err %d (%s) reading from server shell\n\0"
+/* 7242 */ "\n"
+ "Server Restart\n\0"
+/* 7259 */ "(result discarded)\n\0"
+/* 7279 */ "PS4=>${FUNCNAME:-ag}> \0"
+/* 7302 */ "set -x\n"
+ "trap\n"
+ "echo server setup done\n\0"
+/* 7338 */ "ShElL-OuTpUt-HaS-bEeN-cOmPlEtEd\0"
+/* 7370 */ "# %s\0"
+/* 7377 */ "# Makefile dependency file created by:\t\t-*- Mode: Makefile -*-\n"
+ "# %s\n"
+ "# with the following command line arguments:\n\0"
+/* 7491 */ "fopen for write\0"
+/* 7507 */ "'(\0"
+/* 7510 */ "strdup of %d byte string failed\n\0"
+/* 7543 */ "cannot map unprintable chars to C name chars\0"
+/* 7588 */ "ag_scm_string_to_c_name_x\0"
+/* 7614 */ "syscall\0"
+/* 7622 */ "\tfrom %s line %d\n\0"
+/* 7640 */ ".d-XXXXXX\0"
+/* 7650 */ "Server shell timed out 5 times\0"
+/* 7681 */ "Template macro %s invoked with %d args\n\0"
+/* 7721 */ "template\0"
+/* 7730 */ "Warning in template %s, line %d\n"
+ "\t%s\n\0"
+/* 7767 */ "add source dep: %s\n\0"
+/* 7788 */ "add target dep: %s\n\0"
+/* 7809 */ "Adding ``%s'' to environment\n\0"
+/* 7839 */ " '%s'\0"
+/* 7845 */ "eval for arg %d:\n"
+ "\t`%s'\n\0"
+/* 7869 */ "CASE string `%s' did not match\n\0"
+/* 7901 */ "CASE string `%s' %s matched `%s'\n\0"
+/* 7935 */ "CASE no match: `%s' %s vs. `%s'\n\0"
+/* 7968 */ "marker ``%s'' loaded\n\0"
+/* 7990 */ "Definition Load:\n\0"
+/* 8008 */ "eval from file %s line %d:\n"
+ "%s\n\0"
+/* 8039 */ " in \"%s\" -- \0"
+/* 8052 */ "FOR %s loop in %s on line %d begins:\n\0"
+/* 8090 */ "FOR %s repeated %d times\n\0"
+/* 8116 */ "FOR loop skipped - no definition for `%s'\n\0"
+/* 8159 */ "IF expression `%s' on line %d yielded true\n\0"
+/* 8203 */ "IF `%s' macro selected no clause\n\0"
+/* 8237 */ "Template %s included\n\0"
+/* 8259 */ "remapped to '%s' (%2X) in %s at line %d\n\0"
+/* 8300 */ "\tbased on %s\n\0"
+/* 8314 */ "Expr\0"
+/* 8319 */ "Invoke\0"
+/* 8326 */ "WHILE macro repeated %d times\n\0"
+/* 8357 */ "WHILE `%s' loop in %s on line %d begins:\n\0"
+/* 8399 */ "\texiting FOR %s from %d to %d by %d:\n"
+ "\tmore than %d iterations\n\0"
+/* 8462 */ "too many FOR iterations in %s line %d\n\0"
+/* 8501 */ "Defining macro %s from %s\n\0"
+/* 8528 */ "%-10s (%2X) in %s at line %d\n\0"
+/* 8558 */ "searching for `%s'\0"
+/* 8577 */ "%s %s to %s\n\0"
+/* 8590 */ "%s '%s' mode %s\n\0"
+/* 8607 */ "NOTE: skipping file '%s'\n\0"
+/* 8634 */ "%s -- temp file %s\n\0"
+/* 8654 */ "%s from %s to '%s'\n\0"
+/* 8674 */ "%s %s%s\n\0"
+/* 8683 */ "%s %s from '%s'\n\0"
+/* 8700 */ "remove source dep: %s\n\0"
+/* 8724 */ "remove target dep: %s\n\0"
+/* 8748 */ "Compiling ``%s'' with bits 0x%lX\n\0"
+/* 8782 */ "\n"
+ "Server First Start\n\0"
+/* 8803 */ "Server shell is pid %u\n\0"
+/* 8827 */ "\n"
+ "= = = RESULT %d bytes:\n"
+ "%s%s\n"
+ "= = = = = = = = = = = = = = =\n\0"
+/* 8887 */ "Server shell %s starts\n\0"
+/* 8911 */ "\n"
+ "AutoGen %u starts: %s\0"
+/* 8935 */ "%s %s as '%s'\n\0"
+/* 8950 */ "Trap state:\n"
+ "%s\n\0"
+/* 8966 */ "\tcode %lX -- %s\n\0"
+/* 8983 */ "0x%016llX <<== '%s'\n\0"
+/* 9004 */ "Server traps set\n\0"
+/* 9022 */ "true\0"
+/* 9027 */ "uname(2)\0"
+/* 9036 */ "invalid make dependency option: %s\0"
+/* 9072 */ "echo 'definition names looked up'\n"
+ "sed 's/[-^]/-/g'|sort -u -f\n"
+ "echo 'end of looked up definitions'\0"
+/* 9170 */ "%s\n\0"
+/* 9174 */ "Warning\0"
+/* 9182 */ "format\0"
+/* 9189 */ "yes\0"
+/* 9193 */ "'%s'\n\0"
+/* 9199 */ "%s: in %s on line %d\n"
+ "\ttoken in error: %s\n"
+ "\t[[...<error-text>]] %s\n\n"
+ "Likely causes: a mismatched quote, a value that needs quoting,\n"
+ "\t\tor a missing semi-colon\n\0"
+/* 9358 */ "%s: ''%s''\n\0"
+/* 9371 */ "unterminated quote in definition\0"
+/* 9404 */ "\0"
+/* 9405 */ "(use-modules (ice-9 common-list))\n"
+ "(define identifier?\n"
+ "(lambda (x) (or (string? x) (symbol? x))))\n"
+ "(define normalize-identifier\n"
+ "(lambda (x)\n"
+ "(if (string? x) (string->symbol x) x)))\n"
+ "(define coerce->string\n"
+ "(lambda (x)\n"
+ "(let ((char->string (lambda (x) (make-string 1 x)))\n"
+ "(coercable? (lambda (x)\n"
+ "(or (string? x) (boolean? x) (char? x)\n"
+ "(symbol? x) (list? x) (number? x)))))\n"
+ "(if (not (coercable? x))\n"
+ "(error \"Wrong type to coerce->string\" x))\n"
+ "(cond\n"
+ "((string? x) (string-append\n"
+ "(char->string #\\\") x (char->string #\\\")))\n"
+ "((boolean? x) (if x \"#t\" \"#f\"))\n"
+ "((char? x) (char->string x))\n"
+ "((number? x) (number->string x))\n"
+ "((symbol? x) (symbol->string x))\n"
+ "((list? x) (if (every coercable? x)\n"
+ "(apply string-append (map coerce->string x))))\n"
+ "))))\n"
+ "(define alist->autogen-def\n"
+ "(lambda (lst . recursive)\n"
+ "(if (null? recursive) (set! recursive #f)\n"
+ "(set! recursive #t))\n"
+ "(let ((res (if recursive \"{\\n\" \"\"))\n"
+ "(list-nnul? (lambda (x) (and (list? x) (not (null? x))))))\n"
+ "(do ((i lst (cdr i)))\n"
+ "((null? i) (if recursive\n"
+ "(string-append res \"}\")\n"
+ "res))\n"
+ "(let* ((kvpair (car i))\n"
+ "(value (cdr kvpair))\n"
+ "(value-is-alist (if (and (list-nnul? value)\n"
+ "(list-nnul? (car value))\n"
+ "(list-nnul? (caar value))\n"
+ "(identifier? (caaar value)))\n"
+ "#t #f)))\n"
+ "(set! res (string-append res\n"
+ "(coerce->string (normalize-identifier (car kvpair)))\n"
+ "\" = \"\n"
+ "(if value-is-alist\n"
+ "(alist->autogen-def (car value) 1)\n"
+ "(coerce->string (cdr kvpair)))\n"
+ "\";\\n\"\n"
+ ")))))))\n"
+ "(define shell-cleanup \"\")\n"
+ "(define add-cleanup (lambda (t)\n"
+ "(set! shell-cleanup (string-append shell-cleanup \"\\n\" t \"\\n\"))))\n"
+ "(define tmp-dir \"\")\n"
+ "(define header-file \"\")\n"
+ "(define header-guard \"\")\n"
+ "(define autogen-version \"5.16.2\")\n"
+ "(define c-file-line-fmt \"#line %2$d \\\"%1$s\\\"\\n\")\n"
+ "(define-macro (defined-as predicate symbol)\n"
+ "`(and (defined? ',symbol) (,predicate ,symbol)))\n"
+ "(define html-escape-encode (lambda (str)\n"
+ "(string-substitute str\n"
+ "'(\"&\" \"<\" \">\")\n"
+ "'(\"&amp;\" \"&lt;\" \"&gt;\"))))\n"
+ "(define stt-table (make-hash-table 31))\n"
+ "(define stt-curr stt-table)\n"
+ "(define stt-idx-tbl stt-table)\n"
+ "(define stt-idx 0)\n"
+ "(define string-table-new (lambda (st-name) (begin\n"
+ "(set! stt-curr (make-hash-table 31))\n"
+ "(hash-create-handle! stt-table st-name stt-curr)\n"
+ "(out-push-new)\n"
+ "(out-suspend st-name)\n"
+ "(set! stt-idx-tbl (make-hash-table 31))\n"
+ "(hash-create-handle! stt-curr \"string-indexes\" stt-idx-tbl)\n"
+ "(hash-create-handle! stt-curr \"current-index\" 0)\n"
+ "\"\"\n"
+ ")))\n"
+ "(define string-table-add (lambda (st-name str-val) (begin\n"
+ "(set! stt-curr (hash-ref stt-table st-name))\n"
+ "(set! stt-idx-tbl (hash-ref stt-curr \"string-indexes\"))\n"
+ "(set! stt-idx (hash-ref stt-idx-tbl str-val))\n"
+ "(if (not (number? stt-idx))\n"
+ "(begin\n"
+ "(set! stt-idx (hash-ref stt-curr \"current-index\"))\n"
+ "(ag-fprintf st-name \"/* %5d */ %s \\\"\\\\0\\\"\\n\"\n"
+ "stt-idx (c-string str-val))\n"
+ "(hash-create-handle! stt-idx-tbl str-val stt-idx)\n"
+ "(hash-set! stt-curr \"current-index\"\n"
+ "(+ stt-idx (string-length str-val) 1))\n"
+ "))\n"
+ "stt-idx\n"
+ ")))\n"
+ "(define string-table-add-ref (lambda (st-name str-val)\n"
+ "(string-append st-name \"+\"\n"
+ "(number->string (string-table-add st-name str-val)))))\n"
+ "(define emit-string-table (lambda (st-name) (begin\n"
+ "(set! stt-curr (hash-ref stt-table st-name))\n"
+ "(set! stt-idx (hash-ref stt-curr \"current-index\"))\n"
+ "(ag-fprintf 0 \"\\nstatic char const %s[%d] =\\n\" st-name stt-idx)\n"
+ "(out-resume st-name)\n"
+ "(emit (shell (string-append\n"
+ "\"sed 's/^ / /\n"
+ "$s/\\\" \\\"\\\\\\\\0\\\"/\\\";/\n"
+ "s/\\\" \\\"\\\\\\\\0/\\\\\\\\0/\n"
+ "' <<\\\\_EOF_\\n\"\n"
+ "(out-pop #t)\n"
+ "\"_EOF_\")))\n"
+ "(emit \"\\n\")\n"
+ ")))\n"
+ "(define string-table-size (lambda (st-name)\n"
+ "(hash-ref (hash-ref stt-table st-name) \"current-index\")))\n"
+ "(define gperf-code (lambda (gp-name) (shellf\n"
+ "\"sed -e '1,/^#line/d' \\\n"
+ "-e '/#include/d' \\\n"
+ "-e '/#line/d' \\\n"
+ "-e '/^[ \\t]*$/d' \\\n"
+ "-e 's/^const struct /static const struct /' \\\n"
+ "-e '/^int main(/,$d' ${gpdir}/%s.c\"\n"
+ "gp-name\n"
+ ")))\n"
+ "(define stack-join (lambda (j-str ag-name)\n"
+ "(join j-str (stack ag-name))))\0"
+/* 13165 */ "exec 8>&2 2>/dev/null\n\n"
+ "if test -n \"${ZSH_VERSION+set}\" && (emulate sh) 1>&2\n"
+ "then\n"
+ " emulate sh\n"
+ " NULLCMD=:\n\n"
+ "else case `set -o` in *posix*) set -o posix ;; esac\n"
+ "fi\n"
+ "trap_exit() {\n"
+ " case \"$1\" in\n"
+ " 0 | 10 | 15 )\n"
+ " exec 1>&- 2>&-\n"
+ " test -d \"${tmp_dir}\" && rm -rf \"${tmp_dir}\"\n"
+ " ;;\n\n"
+ " * )\n"
+ " exec 1>&8\n"
+ " echo \"trapped on signal ${1}\"\n"
+ " test -d \"${tmp_dir}\" && \\\n"
+ " echo \"temp directory has been retained: ${tmp_dir}\"\n"
+ " esac\n"
+ "}\n"
+ "for f in 0 1 2 5 6 7 10 13 14 15\n"
+ "do trap \"trap_exit ${f}\" $f ; done\n\n"
+ "test -n \"${CDPATH}\" && {\n"
+ " CDPATH=''\n"
+ " unset CDPATH\n"
+ "}\n"
+ "( exec 1>&2 ; unalias cd ) && unalias cd\n"
+ "die() {\n"
+ " echo \"Killing AutoGen ${AG_pid}\"\n"
+ " echo \"FAILURE REASON: $*\"\n"
+ " kill -15 ${AG_pid}\n"
+ " kill -1 ${AG_pid}\n"
+ " kill -2 ${AG_pid}\n"
+ " exit 1\n"
+ "} >&8\n"
+ "test -z \"${TMPDIR}\" && TMPDIR=/tmp\n"
+ "tmp_dir=''\n"
+ "mk_tmp_dir() {\n"
+ " test -d \"${tmp_dir}\" && return 0\n"
+ " tmp_dir=`\n"
+ " t=\\`mktemp -d ${TMPDIR}/.ag-XXXXXX\\`\n"
+ " test -d \"${t}\" || {\n"
+ " t=${TMPDIR}/.ag-$$\n"
+ " rm -rf ${t}\n"
+ " mkdir ${t} || die cannot mkdir ${t}\n"
+ " }\n"
+ " chmod 700 ${t} || die cannot chmod 700 ${t}\n"
+ " echo ${t}\n"
+ " ` 2>/dev/null\n"
+ "}\n"
+ "exec 2>&8\n"
+ "export AG_pid\n"
+ "AG_pid=\"%u\"\n"
+ "AGexe='%s'\0"
+/* 14344 */ "gperf --version > /dev/null 2>&1 || die 'no gperf program'\n"
+ "test -z ${gpdir} && {\n"
+ " gpdir=`mktemp -d ./.gperf.XXXXXX` 2>/dev/null\n"
+ " test -z \"${gpdir}\" && gpdir=.gperf.$$\n"
+ " test -d ${gpdir} || mkdir ${gpdir} || die \"cannot mkdir ${gpdir}\"\n"
+ "}\n"
+ "cd ${gpdir} || die cannot cd into ${gpdir}\n"
+ "gpdir=`pwd`\n"
+ "gperf_%2$s=${gpdir}/%2$s\n\n"
+ "{\n"
+ " cat << \\_EOF_\n"
+ "%%{\n"
+ "#include <stdio.h>\n"
+ "%%}\n"
+ "struct %2$s_index { char const * name; int const idx; };\n"
+ "%%%%\n"
+ "_EOF_\n\n"
+ " idx=1\n"
+ " while read f\n"
+ " do echo \"${f}, ${idx}\"\n"
+ " idx=`expr ${idx} + 1`\n"
+ " done << \\_EOLIST_\n"
+ "%3$s\n"
+ "_EOLIST_\n\n"
+ " cat << '_EOF_'\n"
+ "%%%%\n"
+ "int main(int argc, char** argv) {\n"
+ " char* pz = argv[1];\n"
+ " struct %2$s_index const * pI = %2$s_find( pz, strlen( pz ));\n"
+ " if (pI == NULL)\n"
+ " return 1;\n"
+ " printf( \"0x%%02X\\n\", pI->idx );\n"
+ " return 0;\n"
+ "}\n"
+ "_EOF_\n"
+ "} > %2$s.gperf\n\n"
+ "exec 2> %2$s.log\n"
+ "gperf --language=ANSI-C -H %2$s_hash -N %2$s_find \\\n"
+ " -C -E -I -t %2$s.gperf > %2$s-temp.c || \\\n"
+ " die \"gperf failed on ${gpdir}/%2$s.gperf\n"
+ " `cat %2$s.log`\"\n"
+ "egrep -v '^[^#].*_inline' %2$s-temp.c > %2$s.c\n"
+ "export CFLAGS=-g\n"
+ "%1$s %2$s 1>&2\n"
+ "test $? -eq 0 -a -x ${gperf_%2$s} || \\\n"
+ " die \"could not '%1$s %2$s' gperf program\n"
+ " `cat %2$s.log`\"\n"
+ "exec 2>&8\0"
+/* 15524 */ "test -n \"${gperf_%1$s}\" || \\\n"
+ " die 'no environment variable \"gperf_%1$s\"'\n"
+ "test -x \"${gperf_%1$s}\" || \\\n"
+ " die \"no gperf program named ${gperf_%1$s}\"\n"
+ "${gperf_%1$s} %2$s";
+
+/* end of ag-text.c */
diff --git a/agen5/ag-text.h b/agen5/ag-text.h
new file mode 100644
index 0000000..24c3daf
--- /dev/null
+++ b/agen5/ag-text.h
@@ -0,0 +1,856 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (ag-text.h)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:41 AM by AutoGen 5.16.2pre7
+ * From the definitions /old-home/bkorb/ag/ag/agen5/ag-text.def
+ * and the template file strings
+ *
+ * Copyright (C) 2011-2012 Bruce Korb, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the
+ * Modified (3 clause) Berkeley Software Distribution License
+ * <http://www.xfree86.org/3.3.6/COPYRIGHT2.html>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name ``Bruce Korb'' nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * strings IS PROVIDED BY Bruce Korb ``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 Bruce Korb OR ANY OTHER 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 STRINGS_AG_TEXT_H_GUARD
+#define STRINGS_AG_TEXT_H_GUARD 1
+/*
+ * 404 strings in ag_text_strtable string table
+ */
+#define ADD_ENV_VARS_SUFFIX_FMT (ag_text_strtable+10)
+#define ADD_ENV_VARS_SUFFIX_FMT_LEN 4
+#define ADD_SYS_ENV_VAL_FMT (ag_text_strtable+15)
+#define ADD_SYS_ENV_VAL_FMT_LEN 4
+#define AG_ABEND_STATE_FMT (ag_text_strtable+20)
+#define AG_ABEND_STATE_FMT_LEN 22
+#define AG_ABORT_LOC_FMT (ag_text_strtable+43)
+#define AG_ABORT_LOC_FMT_LEN 74
+#define AG_FPRINTF_BAD_PORT (ag_text_strtable+118)
+#define AG_FPRINTF_BAD_PORT_LEN 29
+#define AG_MAKE_DEP_NAME (ag_text_strtable+148)
+#define AG_MAKE_DEP_NAME_LEN 16
+#define AG_MARK (ag_text_strtable+165)
+#define AG_MARK_LEN 8
+#define AG_SIG_ABORT_FMT (ag_text_strtable+174)
+#define AG_SIG_ABORT_FMT_LEN 47
+#define AG_TEXT_STRTABLE_FILE (ag_text_strtable+0)
+#define AG_TEXT_STRTABLE_FILE_LEN 9
+#define ALIST_TO_AG_ERR (ag_text_strtable+222)
+#define ALIST_TO_AG_ERR_LEN 52
+#define ALIST_TO_AG_TEXT (ag_text_strtable+275)
+#define ALIST_TO_AG_TEXT_LEN 27
+#define ALIST_TO_AG_WRAP (ag_text_strtable+303)
+#define ALIST_TO_AG_WRAP_LEN 23
+#define APRF_ALLOCATE_FAIL (ag_text_strtable+327)
+#define APRF_ALLOCATE_FAIL_LEN 50
+#define ASPRINTFV_FAIL_FMT (ag_text_strtable+378)
+#define ASPRINTFV_FAIL_FMT_LEN 26
+#define ASSEMBLE_NAME_ERR (ag_text_strtable+405)
+#define ASSEMBLE_NAME_ERR_LEN 51
+#define BAD_BREAK_FMT (ag_text_strtable+457)
+#define BAD_BREAK_FMT_LEN 29
+#define BAD_MAC_NM_FMT (ag_text_strtable+487)
+#define BAD_MAC_NM_FMT_LEN 36
+#define BAD_MSG_STR (ag_text_strtable+524)
+#define BAD_MSG_STR_LEN 36
+#define BAD_NAME_FMT (ag_text_strtable+561)
+#define BAD_NAME_FMT_LEN 37
+#define BAD_RE_FMT (ag_text_strtable+599)
+#define BAD_RE_FMT_LEN 46
+#define BOGUS_TAG (ag_text_strtable+646)
+#define BOGUS_TAG_LEN 11
+#define BREAK_STR (ag_text_strtable+658)
+#define BREAK_STR_LEN 5
+#define BUILD_DEFS_LONG_FMT (ag_text_strtable+664)
+#define BUILD_DEFS_LONG_FMT_LEN 3
+#define CANNOT_FMT (ag_text_strtable+668)
+#define CANNOT_FMT_LEN 28
+#define CFGDEP_DUP_TARGET_MSG (ag_text_strtable+697)
+#define CFGDEP_DUP_TARGET_MSG_LEN 21
+#define CFGDEP_UNKNOWN_DEP_FMT (ag_text_strtable+719)
+#define CFGDEP_UNKNOWN_DEP_FMT_LEN 28
+#define CGI_ERR_MSG_FMT (ag_text_strtable+748)
+#define CGI_ERR_MSG_FMT_LEN 57
+#define CGI_PARSE_ERR_FMT (ag_text_strtable+806)
+#define CGI_PARSE_ERR_FMT_LEN 22
+#define CGI_TEMP_ERR_FILE_STR (ag_text_strtable+829)
+#define CGI_TEMP_ERR_FILE_STR_LEN 22
+#define CMD_FAIL_FMT (ag_text_strtable+852)
+#define CMD_FAIL_FMT_LEN 44
+#define COMPILE_RE_BAD (ag_text_strtable+897)
+#define COMPILE_RE_BAD_LEN 22
+#define CONTINUE_STR (ag_text_strtable+920)
+#define CONTINUE_STR_LEN 8
+#define C_STRING_NEWLINE (ag_text_strtable+929)
+#define C_STRING_NEWLINE_LEN 12
+#define DEF_ERR_FMT (ag_text_strtable+942)
+#define DEF_ERR_FMT_LEN 31
+#define DEF_FILE_LINE_FMT (ag_text_strtable+974)
+#define DEF_FILE_LINE_FMT_LEN 15
+#define DEF_NOTE_FMT (ag_text_strtable+990)
+#define DEF_NOTE_FMT_LEN 41
+#define DEF_TYPE_BLOCK (ag_text_strtable+1032)
+#define DEF_TYPE_BLOCK_LEN 5
+#define DEF_TYPE_INVALID (ag_text_strtable+1038)
+#define DEF_TYPE_INVALID_LEN 7
+#define DEF_TYPE_TEXT (ag_text_strtable+1046)
+#define DEF_TYPE_TEXT_LEN 4
+#define DEF_TYPE_UNKNOWN (ag_text_strtable+1051)
+#define DEF_TYPE_UNKNOWN_LEN 7
+#define DEP_FILE_CLEAN_FMT (ag_text_strtable+1069)
+#define DEP_FILE_CLEAN_FMT_LEN 91
+#define DEP_FILE_SPLICE_STR (ag_text_strtable+1059)
+#define DEP_FILE_SPLICE_STR_LEN 3
+#define DEP_FILE_TARG_FMT (ag_text_strtable+1063)
+#define DEP_FILE_TARG_FMT_LEN 5
+#define DEP_FILE_WRAP_FMT (ag_text_strtable+1161)
+#define DEP_FILE_WRAP_FMT_LEN 48
+#define DEP_List (ag_text_strtable+1210)
+#define DEP_List_LEN 6
+#define DEP_OUT_NAME (ag_text_strtable+1241)
+#define DEP_OUT_NAME_LEN 19
+#define DEP_SList (ag_text_strtable+1217)
+#define DEP_SList_LEN 12
+#define DEP_TList (ag_text_strtable+1230)
+#define DEP_TList_LEN 10
+#define DEV_NULL (ag_text_strtable+1261)
+#define DEV_NULL_LEN 9
+#define DFT_BASE_NAME (ag_text_strtable+1271)
+#define DFT_BASE_NAME_LEN 8
+#define DFT_DEF_INPUT_STR (ag_text_strtable+1280)
+#define DFT_DEF_INPUT_STR_LEN 1
+#define DFT_ENV_VAL (ag_text_strtable+1282)
+#define DFT_ENV_VAL_LEN 2
+#define DFT_TPL_DIR_DATA (ag_text_strtable+1285)
+#define DFT_TPL_DIR_DATA_LEN 2
+#define DFT_TPL_DIR_RELATIVE (ag_text_strtable+1288)
+#define DFT_TPL_DIR_RELATIVE_LEN 19
+#define DIRECT_ASSERT_FMT (ag_text_strtable+1308)
+#define DIRECT_ASSERT_FMT_LEN 27
+#define DIRECT_CK_LIST_MARK (ag_text_strtable+1336)
+#define DIRECT_CK_LIST_MARK_LEN 2
+#define DIRECT_ELIF_BAD_FMT (ag_text_strtable+1339)
+#define DIRECT_ELIF_BAD_FMT_LEN 63
+#define DIRECT_ELSE_BAD (ag_text_strtable+1403)
+#define DIRECT_ELSE_BAD_LEN 4
+#define DIRECT_ENDIF_BAD (ag_text_strtable+1408)
+#define DIRECT_ENDIF_BAD_LEN 5
+#define DIRECT_ENDMAC_BAD (ag_text_strtable+1414)
+#define DIRECT_ENDMAC_BAD_LEN 6
+#define DIRECT_ENDSHELL_BAD (ag_text_strtable+1421)
+#define DIRECT_ENDSHELL_BAD_LEN 8
+#define DIRECT_ERROR_FMT (ag_text_strtable+1430)
+#define DIRECT_ERROR_FMT_LEN 41
+#define DIRECT_INC_CANNOT_FIND (ag_text_strtable+1472)
+#define DIRECT_INC_CANNOT_FIND_LEN 44
+#define DIRECT_INC_CANNOT_OPEN (ag_text_strtable+1517)
+#define DIRECT_INC_CANNOT_OPEN_LEN 4
+#define DIRECT_INC_CANNOT_READ (ag_text_strtable+1522)
+#define DIRECT_INC_CANNOT_READ_LEN 4
+#define DIRECT_INC_CANNOT_STAT (ag_text_strtable+1527)
+#define DIRECT_INC_CANNOT_STAT_LEN 47
+#define DIRECT_INC_DEF_SFX (ag_text_strtable+1575)
+#define DIRECT_INC_DEF_SFX_LEN 3
+#define DIRECT_INC_NOT_REG (ag_text_strtable+1579)
+#define DIRECT_INC_NOT_REG_LEN 47
+#define DIRECT_NOENDIF_FMT (ag_text_strtable+1627)
+#define DIRECT_NOENDIF_FMT_LEN 51
+#define DIRECT_NOMATCH_FMT (ag_text_strtable+1679)
+#define DIRECT_NOMATCH_FMT_LEN 69
+#define DIRECT_SHELL_COMP_DEFS (ag_text_strtable+1749)
+#define DIRECT_SHELL_COMP_DEFS_LEN 20
+#define DIRECT_SHELL_END_SHELL (ag_text_strtable+1770)
+#define DIRECT_SHELL_END_SHELL_LEN 10
+#define DIRECT_SHELL_NOEND (ag_text_strtable+1781)
+#define DIRECT_SHELL_NOEND_LEN 50
+#define DONE_CHECK_DONE (ag_text_strtable+1832)
+#define DONE_CHECK_DONE_LEN 16
+#define DONE_CHECK_REDONE (ag_text_strtable+1849)
+#define DONE_CHECK_REDONE_LEN 19
+#define DOOPT_TOO_MANY_DEFS (ag_text_strtable+1869)
+#define DOOPT_TOO_MANY_DEFS_LEN 37
+#define DOT_SFX_FMT (ag_text_strtable+1907)
+#define DOT_SFX_FMT_LEN 5
+#define DOUBLE_NEWLINE (ag_text_strtable+1913)
+#define DOUBLE_NEWLINE_LEN 2
+#define DO_STDOUT_TPL_ABANDONED (ag_text_strtable+1916)
+#define DO_STDOUT_TPL_ABANDONED_LEN 23
+#define DO_STDOUT_TPL_BADR (ag_text_strtable+1940)
+#define DO_STDOUT_TPL_BADR_LEN 27
+#define DO_STDOUT_TPL_CONTENT (ag_text_strtable+1968)
+#define DO_STDOUT_TPL_CONTENT_LEN 25
+#define DO_STDOUT_TPL_NOSFX (ag_text_strtable+1994)
+#define DO_STDOUT_TPL_NOSFX_LEN 8
+#define DO_STDOUT_TPL_START_STD (ag_text_strtable+2003)
+#define DO_STDOUT_TPL_START_STD_LEN 25
+#define DO_STDOUT_TPL_STDOUT (ag_text_strtable+2029)
+#define DO_STDOUT_TPL_STDOUT_LEN 6
+#define DO_SUFFIX_BAD_CHARS (ag_text_strtable+2036)
+#define DO_SUFFIX_BAD_CHARS_LEN 35
+#define DO_SUFFIX_EMPTY (ag_text_strtable+2072)
+#define DO_SUFFIX_EMPTY_LEN 19
+#define EMIT_INVAL_PORT (ag_text_strtable+2092)
+#define EMIT_INVAL_PORT_LEN 25
+#define END_C_COMMENT (ag_text_strtable+2118)
+#define END_C_COMMENT_LEN 2
+#define ERROR_IN_TPL_FMT (ag_text_strtable+2121)
+#define ERROR_IN_TPL_FMT_LEN 31
+#define ERROR_STR (ag_text_strtable+2153)
+#define ERROR_STR_LEN 5
+#define EVAL_EXPR_BLOCK_IN_EVAL (ag_text_strtable+2159)
+#define EVAL_EXPR_BLOCK_IN_EVAL_LEN 47
+#define EVAL_EXPR_PROG_ERR (ag_text_strtable+2207)
+#define EVAL_EXPR_PROG_ERR_LEN 35
+#define EVAL_TRUE_FALSE_STR (ag_text_strtable+2243)
+#define EVAL_TRUE_FALSE_STR_LEN 5
+#define EXIT_CLEANUP_DONE_FMT (ag_text_strtable+2249)
+#define EXIT_CLEANUP_DONE_FMT_LEN 38
+#define EXIT_CLEANUP_MULLIGAN (ag_text_strtable+2288)
+#define EXIT_CLEANUP_MULLIGAN_LEN 21
+#define EXIT_CLEANUP_NOWAIT (ag_text_strtable+2310)
+#define EXIT_CLEANUP_NOWAIT_LEN 10
+#define EXIT_CLEANUP_STR (ag_text_strtable+2321)
+#define EXIT_CLEANUP_STR_LEN 93
+#define EXIT_CLEANUP_WAITED (ag_text_strtable+2415)
+#define EXIT_CLEANUP_WAITED_LEN 6
+#define EXP_FMT_BAD_LIC (ag_text_strtable+2422)
+#define EXP_FMT_BAD_LIC_LEN 18
+#define EXP_FMT_DNE (ag_text_strtable+2441)
+#define EXP_FMT_DNE1 (ag_text_strtable+2582)
+#define EXP_FMT_DNE1_LEN 46
+#define EXP_FMT_DNE2 (ag_text_strtable+2629)
+#define EXP_FMT_DNE2_LEN 145
+#define EXP_FMT_DNE_LEN 140
+#define EXTRACT_CAVEAT (ag_text_strtable+2775)
+#define EXTRACT_CAVEAT_LEN 26
+#define EXTRACT_END (ag_text_strtable+2802)
+#define EXTRACT_END_LEN 5
+#define EXTRACT_START (ag_text_strtable+2808)
+#define EXTRACT_START_LEN 5
+#define FALSE_NAME_STR (ag_text_strtable+2243)
+#define FALSE_NAME_STR_LEN 5
+#define FILE_LINE_FMT (ag_text_strtable+2814)
+#define FILE_LINE_FMT_LEN 13
+#define FILL_IN_VAL_NO_ASSIGN (ag_text_strtable+2828)
+#define FILL_IN_VAL_NO_ASSIGN_LEN 24
+#define FILL_IN_VAL_NO_SEP (ag_text_strtable+2853)
+#define FILL_IN_VAL_NO_SEP_LEN 27
+#define FIND_DIRECT_UNKNOWN (ag_text_strtable+2881)
+#define FIND_DIRECT_UNKNOWN_LEN 51
+#define FIND_ENT_FAIL (ag_text_strtable+2933)
+#define FIND_ENT_FAIL_LEN 7
+#define FIND_ENT_SUCC (ag_text_strtable+2941)
+#define FIND_ENT_SUCC_LEN 8
+#define FIND_FILE_CURDIR (ag_text_strtable+2950)
+#define FIND_FILE_CURDIR_LEN 1
+#define FIND_FILE_DIR_FMT (ag_text_strtable+2952)
+#define FIND_FILE_DIR_FMT_LEN 5
+#define FIND_FILE_NAME (ag_text_strtable+2958)
+#define FIND_FILE_NAME_LEN 9
+#define FIND_LIC_TEXT_AGPL (ag_text_strtable+2968)
+#define FIND_LIC_TEXT_AGPL_LEN 4
+#define FIND_LIC_TEXT_BAD_FILE (ag_text_strtable+2973)
+#define FIND_LIC_TEXT_BAD_FILE_LEN 14
+#define FIND_LIC_TEXT_INVAL (ag_text_strtable+2988)
+#define FIND_LIC_TEXT_INVAL_LEN 24
+#define FIND_LIC_TEXT_LGPL (ag_text_strtable+3013)
+#define FIND_LIC_TEXT_LGPL_LEN 4
+#define FIND_LIC_TEXT_MBSD (ag_text_strtable+3018)
+#define FIND_LIC_TEXT_MBSD_LEN 4
+#define FIND_LIC_TEXT_NO_LIC (ag_text_strtable+3023)
+#define FIND_LIC_TEXT_NO_LIC_LEN 9
+#define FIND_LIC_TEXT_OPEN (ag_text_strtable+1517)
+#define FIND_LIC_TEXT_OPEN_LEN 4
+#define FIND_LIC_TEXT_SFX (ag_text_strtable+3033)
+#define FIND_LIC_TEXT_SFX_LEN 3
+#define FIND_MAC_END_EMPTY (ag_text_strtable+3037)
+#define FIND_MAC_END_EMPTY_LEN 35
+#define FIND_MAC_END_NESTED (ag_text_strtable+3073)
+#define FIND_MAC_END_NESTED_LEN 18
+#define FIND_MAC_END_NOPE (ag_text_strtable+3092)
+#define FIND_MAC_END_NOPE_LEN 16
+#define FN_BOGUS_FMT (ag_text_strtable+3109)
+#define FN_BOGUS_FMT_LEN 55
+#define FN_BOGUS_HUH (ag_text_strtable+3165)
+#define FN_BOGUS_HUH_LEN 13
+#define FN_DEBUG (ag_text_strtable+3179)
+#define FN_DEBUG_GRAPHIC (ag_text_strtable+3210)
+#define FN_DEBUG_GRAPHIC_LEN 5
+#define FN_DEBUG_LEN 30
+#define FN_IF_ELSE (ag_text_strtable+3216)
+#define FN_IF_ELSE_LEN 11
+#define FOR_IN_LISTLESS (ag_text_strtable+3228)
+#define FOR_IN_LISTLESS_LEN 24
+#define GUARD_SFX (ag_text_strtable+3253)
+#define GUARD_SFX_LEN 6
+#define GUILE_CMD_FAIL_FMT (ag_text_strtable+3260)
+#define GUILE_CMD_FAIL_FMT_LEN 73
+#define GUILE_WARN_DEP_STR (ag_text_strtable+3334)
+#define GUILE_WARN_DEP_STR_LEN 21
+#define GUILE_WARN_NO_ENV (ag_text_strtable+3356)
+#define GUILE_WARN_NO_ENV_LEN 24
+#define HANDLE_EOL__ELS (ag_text_strtable+3381)
+#define HANDLE_EOL__ELS_LEN 3
+#define HANDLE_EOL__THE (ag_text_strtable+3385)
+#define HANDLE_EOL__THE_LEN 3
+#define HANDLE_HASH_BAD_TPL (ag_text_strtable+3389)
+#define HANDLE_HASH_BAD_TPL_LEN 21
+#define HANDLE_HASH_ENV_FMT (ag_text_strtable+3411)
+#define HANDLE_HASH_ENV_FMT_LEN 5
+#define HANDLE_HASH_SHELL (ag_text_strtable+3417)
+#define HANDLE_HASH_SHELL_LEN 5
+#define HANDLE_SED_ELSE (ag_text_strtable+3423)
+#define HANDLE_SED_ELSE_LEN 3
+#define HANDLE_SED_ENDIF (ag_text_strtable+3427)
+#define HANDLE_SED_ENDIF_LEN 4
+#define HANDLE_SED_IFDEF (ag_text_strtable+3432)
+#define HANDLE_SED_IFDEF_LEN 5
+#define HANDLE_SED_IFNDEF (ag_text_strtable+3438)
+#define HANDLE_SED_IFNDEF_LEN 6
+#define HEADER_STR (ag_text_strtable+3445)
+#define HEADER_STR_LEN 6
+#define HERE_ENDLESS_STR (ag_text_strtable+3452)
+#define HERE_ENDLESS_STR_LEN 23
+#define HERE_MARK_TOO_LONG (ag_text_strtable+3476)
+#define HERE_MARK_TOO_LONG_LEN 32
+#define HERE_MISS_MARK_STR (ag_text_strtable+3509)
+#define HERE_MISS_MARK_STR_LEN 27
+#define HIDE_EMAIL_END_FMT (ag_text_strtable+3537)
+#define HIDE_EMAIL_END_FMT_LEN 48
+#define HIDE_EMAIL_START_STR (ag_text_strtable+3586)
+#define HIDE_EMAIL_START_STR_LEN 166
+#define HTML_DEC_DIGIT (ag_text_strtable+3753)
+#define HTML_DEC_DIGIT_LEN 5
+#define INIT_SCM_DEBUG_FMT (ag_text_strtable+3759)
+#define INIT_SCM_DEBUG_FMT_LEN 25
+#define INIT_SCM_ERRS_FMT (ag_text_strtable+3785)
+#define INIT_SCM_ERRS_FMT_LEN 125
+#define KR_STRING_NEWLINE (ag_text_strtable+3939)
+#define KR_STRING_NEWLINE_LEN 4
+#define LC_ALL_IS_C (ag_text_strtable+3944)
+#define LC_ALL_IS_C_LEN 8
+#define LD_BOGUS_UNKNOWN (ag_text_strtable+3953)
+#define LD_BOGUS_UNKNOWN_LEN 53
+#define LD_CASE_NO_ESAC (ag_text_strtable+4007)
+#define LD_CASE_NO_ESAC_LEN 14
+#define LD_CASE_NO_EXPR (ag_text_strtable+4022)
+#define LD_CASE_NO_EXPR_LEN 19
+#define LD_DEF_NEED_NAME (ag_text_strtable+4042)
+#define LD_DEF_NEED_NAME_LEN 22
+#define LD_DEF_WOOPS (ag_text_strtable+4065)
+#define LD_DEF_WOOPS_LEN 24
+#define LD_EXPR_BAD_NAME (ag_text_strtable+4090)
+#define LD_EXPR_BAD_NAME_LEN 23
+#define LD_EXPR_NEED_TWO (ag_text_strtable+4114)
+#define LD_EXPR_NEED_TWO_LEN 25
+#define LD_EXPR_NO_SPACE (ag_text_strtable+4140)
+#define LD_EXPR_NO_SPACE_LEN 28
+#define LD_EXPR_NO_TEXT (ag_text_strtable+4169)
+#define LD_EXPR_NO_TEXT_LEN 25
+#define LD_EXTRACT_BAD_READ (ag_text_strtable+4195)
+#define LD_EXTRACT_BAD_READ_LEN 37
+#define LD_EXTRACT_READ_FAIL (ag_text_strtable+4233)
+#define LD_EXTRACT_READ_FAIL_LEN 12
+#define LD_FOR_IN (ag_text_strtable+4246)
+#define LD_FOR_INVALID_VAR (ag_text_strtable+4249)
+#define LD_FOR_INVALID_VAR_LEN 25
+#define LD_FOR_IN_LEN 2
+#define LD_FOR_NAMELESS (ag_text_strtable+4275)
+#define LD_FOR_NAMELESS_LEN 32
+#define LD_FOR_NO_ENDFOR (ag_text_strtable+4308)
+#define LD_FOR_NO_ENDFOR_LEN 16
+#define LD_IF_NO_ENDIF (ag_text_strtable+4325)
+#define LD_IF_NO_ENDIF_LEN 15
+#define LD_INC_NO_FNAME (ag_text_strtable+4341)
+#define LD_INC_NO_FNAME_LEN 38
+#define LD_SEL_EMPTY (ag_text_strtable+4380)
+#define LD_SEL_EMPTY_LEN 16
+#define LD_SEL_INVAL (ag_text_strtable+4397)
+#define LD_SEL_INVAL_LEN 24
+#define LD_UNKNOWN_INVAL_DEF (ag_text_strtable+4090)
+#define LD_UNKNOWN_INVAL_DEF_LEN 23
+#define LD_WHILE_NO_ENDWHILE (ag_text_strtable+4422)
+#define LD_WHILE_NO_ENDWHILE_LEN 18
+#define LD_WHILE_NO_EXPR (ag_text_strtable+4441)
+#define LD_WHILE_NO_EXPR_LEN 20
+#define LINE_CONCAT3_FMT (ag_text_strtable+4462)
+#define LINE_CONCAT3_FMT_LEN 8
+#define LISTEN_PORT_FMT (ag_text_strtable+4471)
+#define LISTEN_PORT_FMT_LEN 31
+#define LOAD_CGI_DEFS_MARKER (ag_text_strtable+4503)
+#define LOAD_CGI_DEFS_MARKER_LEN 21
+#define LOAD_CGI_GET_NAME (ag_text_strtable+4525)
+#define LOAD_CGI_GET_NAME_LEN 3
+#define LOAD_CGI_INVAL_REQ_FMT (ag_text_strtable+4529)
+#define LOAD_CGI_INVAL_REQ_FMT_LEN 34
+#define LOAD_CGI_NO_DATA_MSG (ag_text_strtable+4564)
+#define LOAD_CGI_NO_DATA_MSG_LEN 25
+#define LOAD_CGI_READ_NAME (ag_text_strtable+1522)
+#define LOAD_CGI_READ_NAME_LEN 4
+#define LOAD_CGI_READ_WHAT (ag_text_strtable+4590)
+#define LOAD_CGI_READ_WHAT_LEN 8
+#define LOAD_FILE_SHORT_NAME (ag_text_strtable+4674)
+#define LOAD_FILE_SHORT_NAME_LEN 35
+#define LOAD_MACS_BAD_PARSE (ag_text_strtable+4599)
+#define LOAD_MACS_BAD_PARSE_LEN 33
+#define LOAD_SCM_ENDLESS (ag_text_strtable+4633)
+#define LOAD_SCM_ENDLESS_LEN 40
+#define LOAD_TPL_CANNOT_MAP (ag_text_strtable+4710)
+#define LOAD_TPL_CANNOT_MAP_LEN 13
+#define LOAD_TPL_CANNOT_OPEN (ag_text_strtable+4724)
+#define LOAD_TPL_CANNOT_OPEN_LEN 28
+#define LOAD_TPL_CANNOT_STAT (ag_text_strtable+3023)
+#define LOAD_TPL_CANNOT_STAT_LEN 9
+#define LOAD_TPL_IRREGULAR (ag_text_strtable+4753)
+#define LOAD_TPL_IRREGULAR_LEN 16
+#define LOAD_TPL_SFX_AGL (ag_text_strtable+4770)
+#define LOAD_TPL_SFX_AGL_LEN 3
+#define LOAD_TPL_SFX_TPL (ag_text_strtable+4774)
+#define LOAD_TPL_SFX_TPL_LEN 3
+#define LOG_SEP_FMT (ag_text_strtable+4778)
+#define LOG_SEP_FMT_LEN 31
+#define MAKE_GPERF_CLEANUP (ag_text_strtable+4810)
+#define MAKE_GPERF_CLEANUP_LEN 31
+#define MAKE_SCRIPT_NL (ag_text_strtable+4842)
+#define MAKE_SCRIPT_NL_LEN 5
+#define MALLOC_FAIL_FMT (ag_text_strtable+4848)
+#define MALLOC_FAIL_FMT_LEN 27
+#define MEM_FILE_STR (ag_text_strtable+4876)
+#define MEM_FILE_STR_LEN 11
+#define MKSTEMP_FAIL_FMT (ag_text_strtable+4888)
+#define MKSTEMP_FAIL_FMT_LEN 22
+#define MK_GPERF_SCRIPT (ag_text_strtable+14344)
+#define MK_GPERF_SCRIPT_LEN 1179
+#define MK_HEAD_GUARD_GUARD (ag_text_strtable+4911)
+#define MK_HEAD_GUARD_GUARD_LEN 27
+#define MK_HEAD_GUARD_SCM (ag_text_strtable+4939)
+#define MK_HEAD_GUARD_SCM_LEN 52
+#define MK_LIC_BIG_OWN (ag_text_strtable+4992)
+#define MK_LIC_BIG_OWN_LEN 12
+#define MK_LIC_BIG_PFX (ag_text_strtable+5005)
+#define MK_LIC_BIG_PFX_LEN 13
+#define MK_LIC_BIG_PROG (ag_text_strtable+5019)
+#define MK_LIC_BIG_PROG_LEN 19
+#define MK_LIC_NOT_STR (ag_text_strtable+5039)
+#define MK_LIC_NOT_STR_LEN 28
+#define MK_LIC_NO_LIC (ag_text_strtable+5068)
+#define MK_LIC_NO_LIC_LEN 23
+#define MK_LIC_NO_OPEN (ag_text_strtable+5092)
+#define MK_LIC_NO_OPEN_LEN 32
+#define MK_LIC_OWN (ag_text_strtable+5125)
+#define MK_LIC_OWN_LEN 7
+#define MK_LIC_PFX (ag_text_strtable+5133)
+#define MK_LIC_PFX_LEN 5
+#define MK_LIC_PROG (ag_text_strtable+5139)
+#define MK_LIC_PROG_LEN 9
+#define MK_LIC_SFX (ag_text_strtable+3033)
+#define MK_LIC_SFX_LEN 3
+#define MK_LIC_TOO_BIG_FMT (ag_text_strtable+5149)
+#define MK_LIC_TOO_BIG_FMT_LEN 27
+#define MK_LIC_YRS (ag_text_strtable+5177)
+#define MK_LIC_YRS_LEN 7
+#define MK_STR_OCT_FMT (ag_text_strtable+5185)
+#define MK_STR_OCT_FMT_LEN 5
+#define MK_TMP_DIR_CMD (ag_text_strtable+5191)
+#define MK_TMP_DIR_CMD_LEN 38
+#define NAMED_VALUES_NO_NAME (ag_text_strtable+5230)
+#define NAMED_VALUES_NO_NAME_LEN 17
+#define NAMED_VALUES_WHERE (ag_text_strtable+5248)
+#define NAMED_VALUES_WHERE_LEN 27
+#define NEWLINE (ag_text_strtable+5276)
+#define NEWLINE_LEN 1
+#define NEXT_PM_TOKEN_INVALID (ag_text_strtable+5278)
+#define NEXT_PM_TOKEN_INVALID_LEN 25
+#define NOT_STR_FMT (ag_text_strtable+5351)
+#define NOT_STR_FMT_LEN 26
+#define NO_IF_EXPR (ag_text_strtable+5304)
+#define NO_IF_EXPR_LEN 17
+#define NO_NAME_STR (ag_text_strtable+5322)
+#define NO_NAME_STR_LEN 2
+#define NO_TEMPLATE_ERR_MSG (ag_text_strtable+5325)
+#define NO_TEMPLATE_ERR_MSG_LEN 25
+#define NULL_FILE_NAME_STR (ag_text_strtable+5378)
+#define NULL_FILE_NAME_STR_LEN 14
+#define OPEN_ERROR_FMT (ag_text_strtable+5393)
+#define OPEN_ERROR_FMT_LEN 37
+#define OPEN_OUTPUT_BAD_FMT (ag_text_strtable+5431)
+#define OPEN_OUTPUT_BAD_FMT_LEN 38
+#define OPEN_OUTPUT_NULL (ag_text_strtable+5470)
+#define OPEN_OUTPUT_NULL_LEN 4
+#define OUTPUT_NO_OPEN (ag_text_strtable+5475)
+#define OUTPUT_NO_OPEN_LEN 15
+#define OUTPUT_NO_UNLINK (ag_text_strtable+5491)
+#define OUTPUT_NO_UNLINK_LEN 6
+#define OUTPUT_TEMP_FILE (ag_text_strtable+5498)
+#define OUTPUT_TEMP_FILE_LEN 13
+#define OUT_ADD_INVALID (ag_text_strtable+5512)
+#define OUT_ADD_INVALID_LEN 34
+#define OUT_CLOSE_TRACE_WRAP (ag_text_strtable+5547)
+#define OUT_CLOSE_TRACE_WRAP_LEN 8
+#define OUT_PUSH_NEW_FAIL (ag_text_strtable+5556)
+#define OUT_PUSH_NEW_FAILED (ag_text_strtable+5567)
+#define OUT_PUSH_NEW_FAILED_LEN 34
+#define OUT_PUSH_NEW_FAIL_LEN 10
+#define OUT_RESUME_CANNOT (ag_text_strtable+5602)
+#define OUT_RESUME_CANNOT_LEN 46
+#define OUT_SUSPEND_CANNOT (ag_text_strtable+5649)
+#define OUT_SUSPEND_CANNOT_LEN 47
+#define OUT_SWITCH_FAIL (ag_text_strtable+5697)
+#define OUT_SWITCH_FAIL_LEN 7
+#define PARSE_INPUT_AG_DEF_STR (ag_text_strtable+5705)
+#define PARSE_INPUT_AG_DEF_STR_LEN 25
+#define PIPE_DEFS_STDIN_NAME (ag_text_strtable+5731)
+#define PIPE_DEFS_STDIN_NAME_LEN 5
+#define PIPE_DEFS_STDIN_STR (ag_text_strtable+1280)
+#define PIPE_DEFS_STDIN_STR_LEN 1
+#define PIPE_DUP2_NAME_STR (ag_text_strtable+5737)
+#define PIPE_DUP2_NAME_STR_LEN 4
+#define PIPE_FIFO_IN_NAME_FMT (ag_text_strtable+5742)
+#define PIPE_FIFO_IN_NAME_FMT_LEN 5
+#define PIPE_FIFO_OPEN (ag_text_strtable+1517)
+#define PIPE_FIFO_OPEN_LEN 4
+#define PIPE_FIFO_OUT_NAME_FMT (ag_text_strtable+5748)
+#define PIPE_FIFO_OUT_NAME_FMT_LEN 6
+#define PIPE_FORK_NAME (ag_text_strtable+5755)
+#define PIPE_FORK_NAME_LEN 4
+#define PIPE_MKFIFO_NAME (ag_text_strtable+5760)
+#define PIPE_MKFIFO_NAME_LEN 6
+#define PIPE_POLL_NAME_STR (ag_text_strtable+5767)
+#define PIPE_POLL_NAME_STR_LEN 4
+#define PIPE_WRITE_NAME_STR (ag_text_strtable+5772)
+#define PIPE_WRITE_NAME_STR_LEN 5
+#define PREFIX_FAIL (ag_text_strtable+5778)
+#define PREFIX_FAIL_LEN 19
+#define PREP_INVOKE_NO_NAME (ag_text_strtable+5798)
+#define PREP_INVOKE_NO_NAME_LEN 32
+#define PREP_INVOKE_NO_SEP (ag_text_strtable+5831)
+#define PREP_INVOKE_NO_SEP_LEN 41
+#define PRINT_DEF_SHOW_FMT (ag_text_strtable+5873)
+#define PRINT_DEF_SHOW_FMT_LEN 25
+#define PRINT_DEF_SPACES (ag_text_strtable+5899)
+#define PRINT_DEF_SPACES_LEN 32
+#define PROC_TPL_BOGUS_RET (ag_text_strtable+1940)
+#define PROC_TPL_BOGUS_RET_LEN 27
+#define PROC_TPL_START (ag_text_strtable+5932)
+#define PROC_TPL_START_LEN 21
+#define PSEUDO_MACRO_NAME_STR (ag_text_strtable+5954)
+#define PSEUDO_MACRO_NAME_STR_LEN 12
+#define PSEUDO_MAC_BAD_ENDER (ag_text_strtable+5967)
+#define PSEUDO_MAC_BAD_ENDER_LEN 36
+#define PSEUDO_MAC_BAD_FSM (ag_text_strtable+6004)
+#define PSEUDO_MAC_BAD_FSM_LEN 16
+#define PSEUDO_MAC_BAD_LENGTH (ag_text_strtable+6021)
+#define PSEUDO_MAC_BAD_LENGTH_LEN 29
+#define PSEUDO_MAC_BAD_MODE (ag_text_strtable+6051)
+#define PSEUDO_MAC_BAD_MODE_LEN 24
+#define PSEUDO_MAC_BAD_NOAG5 (ag_text_strtable+6076)
+#define PSEUDO_MAC_BAD_NOAG5_LEN 20
+#define PSEUDO_MAC_BAD_NOEND (ag_text_strtable+6097)
+#define PSEUDO_MAC_BAD_NOEND_LEN 15
+#define PSEUDO_MAC_BAD_NOEOL (ag_text_strtable+6113)
+#define PSEUDO_MAC_BAD_NOEOL_LEN 16
+#define PSEUDO_MAC_BAD_NOSTART (ag_text_strtable+6130)
+#define PSEUDO_MAC_BAD_NOSTART_LEN 17
+#define PSEUDO_MAC_BAD_NOTPL (ag_text_strtable+6148)
+#define PSEUDO_MAC_BAD_NOTPL_LEN 20
+#define PSEUDO_MAC_BAD_PSEUDO (ag_text_strtable+6169)
+#define PSEUDO_MAC_BAD_PSEUDO_LEN 23
+#define PSEUDO_MAC_BAD_STARTER (ag_text_strtable+6193)
+#define PSEUDO_MAC_BAD_STARTER_LEN 36
+#define PSEUDO_MAC_ERR_FMT (ag_text_strtable+6230)
+#define PSEUDO_MAC_ERR_FMT_LEN 41
+#define PSEUDO_MAC_MODE_MARK (ag_text_strtable+6272)
+#define PSEUDO_MAC_MODE_MARK_LEN 3
+#define PSEUDO_MAC_TPL_FILE (ag_text_strtable+6276)
+#define PSEUDO_MAC_TPL_FILE_LEN 15
+#define READY_INPUT_NODEF (ag_text_strtable+6292)
+#define READY_INPUT_NODEF_LEN 20
+#define READY_INPUT_NOT_REG (ag_text_strtable+6313)
+#define READY_INPUT_NOT_REG_LEN 21
+#define READY_INPUT_STAT (ag_text_strtable+6335)
+#define READY_INPUT_STAT_LEN 4
+#define READ_DEF_NO_DEFS (ag_text_strtable+6340)
+#define READ_DEF_NO_DEFS_LEN 28
+#define READ_DEF_OPEN (ag_text_strtable+1517)
+#define READ_DEF_OPEN_LEN 4
+#define READ_DEF_READ (ag_text_strtable+1522)
+#define READ_DEF_READ_LEN 4
+#define REALLOC_FAIL_FMT (ag_text_strtable+6369)
+#define REALLOC_FAIL_FMT_LEN 36
+#define REQUEST_METHOD (ag_text_strtable+6406)
+#define REQUEST_METHOD_LEN 14
+#define RESOLVE_SCM_EXACT (ag_text_strtable+6421)
+#define RESOLVE_SCM_EXACT_LEN 11
+#define RESOLVE_SCM_INEXACT (ag_text_strtable+6433)
+#define RESOLVE_SCM_INEXACT_LEN 13
+#define RESOLVE_SCM_LIST (ag_text_strtable+6447)
+#define RESOLVE_SCM_LIST_LEN 10
+#define RESOLVE_SCM_NUMBER (ag_text_strtable+664)
+#define RESOLVE_SCM_NUMBER_LEN 3
+#define RESOLVE_SCM_PAIR (ag_text_strtable+6458)
+#define RESOLVE_SCM_PAIR_LEN 10
+#define RESOLVE_SCM_PROC (ag_text_strtable+6469)
+#define RESOLVE_SCM_PROC_LEN 23
+#define RESOLVE_SCM_UNKNOWN (ag_text_strtable+6493)
+#define RESOLVE_SCM_UNKNOWN_LEN 13
+#define RESOLVE_SCM_VECTOR (ag_text_strtable+6507)
+#define RESOLVE_SCM_VECTOR_LEN 12
+#define RETURN_FROM_NOWHERE (ag_text_strtable+6520)
+#define RETURN_FROM_NOWHERE_LEN 21
+#define RUN_GPERF_CMD (ag_text_strtable+15524)
+#define RUN_GPERF_CMD_LEN 171
+#define RUN_PRINTF_HUH (ag_text_strtable+3165)
+#define RUN_PRINTF_HUH_LEN 13
+#define SAFE_PRINTF_BAD_ARGS (ag_text_strtable+6542)
+#define SAFE_PRINTF_BAD_ARGS_LEN 19
+#define SAFE_PRINTF_BAD_FMT (ag_text_strtable+6562)
+#define SAFE_PRINTF_BAD_FMT_LEN 44
+#define SCHEME_EVAL_ERR_FMT (ag_text_strtable+6607)
+#define SCHEME_EVAL_ERR_FMT_LEN 71
+#define SCHEME_INIT_DEBUG_1_6 (ag_text_strtable+3927)
+#define SCHEME_INIT_DEBUG_1_6_LEN 11
+#define SCHEME_INIT_DEBUG_2_0 (ag_text_strtable+3911)
+#define SCHEME_INIT_DEBUG_2_0_LEN 15
+#define SCHEME_INIT_TEXT (ag_text_strtable+9405)
+#define SCHEME_INIT_TEXT_LEN 3759
+#define SCM2ZCHARS_BAD_VAL (ag_text_strtable+6679)
+#define SCM2ZCHARS_BAD_VAL_LEN 49
+#define SCM_ERROR_FMT (ag_text_strtable+6729)
+#define SCM_ERROR_FMT_LEN 50
+#define SCM_FALSE_STR (ag_text_strtable+6780)
+#define SCM_FALSE_STR_LEN 2
+#define SCM_LIST_STR (ag_text_strtable+6783)
+#define SCM_LIST_STR_LEN 3
+#define SCM_OUT_POP_EMPTY (ag_text_strtable+6787)
+#define SCM_OUT_POP_EMPTY_LEN 48
+#define SCM_OUT_POP_NO_REREAD (ag_text_strtable+6836)
+#define SCM_OUT_POP_NO_REREAD_LEN 14
+#define SCM_PROC_CAST (ag_text_strtable+6851)
+#define SCM_PROC_CAST_LEN 5
+#define SCM_TRUE_STR (ag_text_strtable+6857)
+#define SCM_TRUE_STR_LEN 2
+#define SET_ORIG_DIR_NO_MEM_MSG (ag_text_strtable+6860)
+#define SET_ORIG_DIR_NO_MEM_MSG_LEN 25
+#define SET_TMP_DIR_CMD (ag_text_strtable+6886)
+#define SET_TMP_DIR_CMD_LEN 86
+#define SET_WRITE_WARN (ag_text_strtable+6973)
+#define SET_WRITE_WARN_LEN 84
+#define SFX_FMT (ag_text_strtable+7058)
+#define SFX_FMT_LEN 4
+#define SHELLF_BAD_ALIST_MSG (ag_text_strtable+7063)
+#define SHELLF_BAD_ALIST_MSG_LEN 23
+#define SHELL_CMD_FMT (ag_text_strtable+7087)
+#define SHELL_CMD_FMT_LEN 28
+#define SHELL_DIE_ON_SIGNAL_FMT (ag_text_strtable+7116)
+#define SHELL_DIE_ON_SIGNAL_FMT_LEN 41
+#define SHELL_INIT_STR (ag_text_strtable+13165)
+#define SHELL_INIT_STR_LEN 1178
+#define SHELL_LAST_CMD_MSG (ag_text_strtable+7158)
+#define SHELL_LAST_CMD_MSG_LEN 22
+#define SHELL_NO_END_MARK_MSG (ag_text_strtable+7181)
+#define SHELL_NO_END_MARK_MSG_LEN 18
+#define SHELL_READ_ERR_FMT (ag_text_strtable+7200)
+#define SHELL_READ_ERR_FMT_LEN 41
+#define SHELL_RESTART_MSG (ag_text_strtable+7242)
+#define SHELL_RESTART_MSG_LEN 16
+#define SHELL_RES_DISCARD_MSG (ag_text_strtable+7259)
+#define SHELL_RES_DISCARD_MSG_LEN 19
+#define SHELL_SET_PS4_FMT (ag_text_strtable+7279)
+#define SHELL_SET_PS4_FMT_LEN 22
+#define SHELL_UNK_LAST_CMD_MSG (ag_text_strtable+3165)
+#define SHELL_UNK_LAST_CMD_MSG_LEN 13
+#define SHELL_XTRACE_CMDS (ag_text_strtable+7302)
+#define SHELL_XTRACE_CMDS_LEN 35
+#define SH_DONE_MARK (ag_text_strtable+7338)
+#define SH_DONE_MARK_LEN 31
+#define START_DEP_ARG_FMT (ag_text_strtable+7370)
+#define START_DEP_ARG_FMT_LEN 6
+#define START_DEP_FILE_FMT (ag_text_strtable+7377)
+#define START_DEP_FILE_FMT_LEN 113
+#define START_DEP_FOPEN_MSG (ag_text_strtable+7491)
+#define START_DEP_FOPEN_MSG_LEN 15
+#define START_SCHEME_LIST (ag_text_strtable+7507)
+#define START_SCHEME_LIST_LEN 2
+#define STDIN_FILE_NAME (ag_text_strtable+5731)
+#define STDIN_FILE_NAME_LEN 5
+#define STRDUP_FAIL_FMT (ag_text_strtable+7510)
+#define STRDUP_FAIL_FMT_LEN 32
+#define STR_TO_C_MAP_FAIL (ag_text_strtable+7543)
+#define STR_TO_C_MAP_FAIL_LEN 44
+#define STR_TO_C_NAME (ag_text_strtable+7588)
+#define STR_TO_C_NAME_LEN 25
+#define SYSCALL_NAME (ag_text_strtable+7614)
+#define SYSCALL_NAME_LEN 7
+#define TAB_FILE_LINE_FMT (ag_text_strtable+7622)
+#define TAB_FILE_LINE_FMT_LEN 17
+#define TEMP_SUFFIX (ag_text_strtable+7640)
+#define TEMP_SUFFIX_LEN 9
+#define TOO_MANY_TIMEOUTS_MSG (ag_text_strtable+7650)
+#define TOO_MANY_TIMEOUTS_MSG_LEN 30
+#define TPL_FILE_LINE_FMT (ag_text_strtable+974)
+#define TPL_FILE_LINE_FMT_LEN 15
+#define TPL_FILE_NEXT_LINE_FMT (ag_text_strtable+2814)
+#define TPL_FILE_NEXT_LINE_FMT_LEN 13
+#define TPL_FILE_TPL (ag_text_strtable+4774)
+#define TPL_FILE_TPL_LEN 3
+#define TPL_INVOKED (ag_text_strtable+7681)
+#define TPL_INVOKED_LEN 39
+#define TPL_MARK (ag_text_strtable+7721)
+#define TPL_MARK_LEN 8
+#define TPL_WARN_FMT (ag_text_strtable+7730)
+#define TPL_WARN_FMT_LEN 36
+#define TRACE_ADD_SRC_FILE_FMT (ag_text_strtable+7767)
+#define TRACE_ADD_SRC_FILE_FMT_LEN 20
+#define TRACE_ADD_TARG_FILE_FMT (ag_text_strtable+7788)
+#define TRACE_ADD_TARG_FILE_FMT_LEN 20
+#define TRACE_ADD_TO_ENV_FMT (ag_text_strtable+7809)
+#define TRACE_ADD_TO_ENV_FMT_LEN 29
+#define TRACE_AG_ARG_FMT (ag_text_strtable+7839)
+#define TRACE_AG_ARG_FMT_LEN 5
+#define TRACE_BUILD_DEFS (ag_text_strtable+7845)
+#define TRACE_BUILD_DEFS_LEN 23
+#define TRACE_CASE_FAIL (ag_text_strtable+7869)
+#define TRACE_CASE_FAIL_LEN 31
+#define TRACE_CASE_MATCHED (ag_text_strtable+7901)
+#define TRACE_CASE_MATCHED_LEN 33
+#define TRACE_CASE_NOMATCH (ag_text_strtable+7935)
+#define TRACE_CASE_NOMATCH_LEN 32
+#define TRACE_COPY_MARK (ag_text_strtable+7968)
+#define TRACE_COPY_MARK_LEN 21
+#define TRACE_DEF_LOAD (ag_text_strtable+7990)
+#define TRACE_DEF_LOAD_LEN 17
+#define TRACE_EVAL_STRING (ag_text_strtable+8008)
+#define TRACE_EVAL_STRING_LEN 30
+#define TRACE_FIND_ENT (ag_text_strtable+8039)
+#define TRACE_FIND_ENT_LEN 12
+#define TRACE_FN_FOR (ag_text_strtable+8052)
+#define TRACE_FN_FOR_LEN 37
+#define TRACE_FN_FOR_REPEAT (ag_text_strtable+8090)
+#define TRACE_FN_FOR_REPEAT_LEN 25
+#define TRACE_FN_FOR_SKIP (ag_text_strtable+8116)
+#define TRACE_FN_FOR_SKIP_LEN 42
+#define TRACE_FN_IF (ag_text_strtable+8159)
+#define TRACE_FN_IF_LEN 43
+#define TRACE_FN_IF_NOTHING (ag_text_strtable+8203)
+#define TRACE_FN_IF_NOTHING_LEN 33
+#define TRACE_FN_INC_LINE (ag_text_strtable+7622)
+#define TRACE_FN_INC_LINE_LEN 17
+#define TRACE_FN_INC_TPL (ag_text_strtable+8237)
+#define TRACE_FN_INC_TPL_LEN 21
+#define TRACE_FN_REMAPPED (ag_text_strtable+8259)
+#define TRACE_FN_REMAPPED_LEN 40
+#define TRACE_FN_REMAP_BASE (ag_text_strtable+8300)
+#define TRACE_FN_REMAP_BASE_LEN 13
+#define TRACE_FN_REMAP_EXPR (ag_text_strtable+8314)
+#define TRACE_FN_REMAP_EXPR_LEN 4
+#define TRACE_FN_REMAP_INVOKE (ag_text_strtable+8319)
+#define TRACE_FN_REMAP_INVOKE_LEN 6
+#define TRACE_FN_WHILE_END (ag_text_strtable+8326)
+#define TRACE_FN_WHILE_END_LEN 30
+#define TRACE_FN_WHILE_START (ag_text_strtable+8357)
+#define TRACE_FN_WHILE_START_LEN 41
+#define TRACE_FOR_BY_STEP (ag_text_strtable+8399)
+#define TRACE_FOR_BY_STEP_LEN 62
+#define TRACE_FOR_STEP_TOO_FAR (ag_text_strtable+8462)
+#define TRACE_FOR_STEP_TOO_FAR_LEN 38
+#define TRACE_MACRO_DEF (ag_text_strtable+8501)
+#define TRACE_MACRO_DEF_LEN 26
+#define TRACE_MACRO_FMT (ag_text_strtable+8528)
+#define TRACE_MACRO_FMT_LEN 29
+#define TRACE_MATCH_VAL (ag_text_strtable+8558)
+#define TRACE_MATCH_VAL_LEN 18
+#define TRACE_MOVE_FMT (ag_text_strtable+8577)
+#define TRACE_MOVE_FMT_LEN 12
+#define TRACE_OPEN_OUT (ag_text_strtable+8590)
+#define TRACE_OPEN_OUT_LEN 16
+#define TRACE_OUT_DELETE (ag_text_strtable+8607)
+#define TRACE_OUT_DELETE_LEN 26
+#define TRACE_OUT_PUSH_NEW (ag_text_strtable+8634)
+#define TRACE_OUT_PUSH_NEW_LEN 19
+#define TRACE_OUT_SWITCH (ag_text_strtable+8654)
+#define TRACE_OUT_SWITCH_LEN 19
+#define TRACE_POP_FMT (ag_text_strtable+8674)
+#define TRACE_POP_FMT_LEN 8
+#define TRACE_RESUME (ag_text_strtable+8683)
+#define TRACE_RESUME_LEN 16
+#define TRACE_RM_SRC_FILE_FMT (ag_text_strtable+8700)
+#define TRACE_RM_SRC_FILE_FMT_LEN 23
+#define TRACE_RM_TARG_FILE_FMT (ag_text_strtable+8724)
+#define TRACE_RM_TARG_FILE_FMT_LEN 23
+#define TRACE_SEL_MATCH_FULL (ag_text_strtable+8748)
+#define TRACE_SEL_MATCH_FULL_LEN 33
+#define TRACE_SHELL_FIRST_START (ag_text_strtable+8782)
+#define TRACE_SHELL_FIRST_START_LEN 20
+#define TRACE_SHELL_PID_FMT (ag_text_strtable+8803)
+#define TRACE_SHELL_PID_FMT_LEN 23
+#define TRACE_SHELL_RESULT_MSG (ag_text_strtable+8827)
+#define TRACE_SHELL_RESULT_MSG_LEN 59
+#define TRACE_SHELL_STARTS_FMT (ag_text_strtable+8887)
+#define TRACE_SHELL_STARTS_FMT_LEN 23
+#define TRACE_START_FMT (ag_text_strtable+8911)
+#define TRACE_START_FMT_LEN 23
+#define TRACE_SUSPEND (ag_text_strtable+8935)
+#define TRACE_SUSPEND_LEN 14
+#define TRACE_TRAP_STATE_FMT (ag_text_strtable+8950)
+#define TRACE_TRAP_STATE_FMT_LEN 15
+#define TRACE_UNKNOWN_FMT (ag_text_strtable+8966)
+#define TRACE_UNKNOWN_FMT_LEN 16
+#define TRACE_VER_CONVERT (ag_text_strtable+8983)
+#define TRACE_VER_CONVERT_LEN 20
+#define TRACE_XTRACE_MSG (ag_text_strtable+9004)
+#define TRACE_XTRACE_MSG_LEN 17
+#define TRUE_NAME_STR (ag_text_strtable+9022)
+#define TRUE_NAME_STR_LEN 4
+#define UNAME_CALL_NAME (ag_text_strtable+9027)
+#define UNAME_CALL_NAME_LEN 8
+#define USAGE_INVAL_DEP_OPT_FMT (ag_text_strtable+9036)
+#define USAGE_INVAL_DEP_OPT_FMT_LEN 35
+#define USED_DEFINES_FMT (ag_text_strtable+9072)
+#define USED_DEFINES_FMT_LEN 97
+#define USED_DEFINES_LINE_FMT (ag_text_strtable+9170)
+#define USED_DEFINES_LINE_FMT_LEN 3
+#define WARN_STR (ag_text_strtable+9174)
+#define WARN_STR_LEN 7
+#define WORD_FORMAT (ag_text_strtable+9182)
+#define WORD_FORMAT_LEN 6
+#define YES_NAME_STR (ag_text_strtable+9189)
+#define YES_NAME_STR_LEN 3
+#define YYLEX_DF_STR (ag_text_strtable+9193)
+#define YYLEX_DF_STR_LEN 5
+#define YYLEX_ERR_FMT (ag_text_strtable+9199)
+#define YYLEX_ERR_FMT_LEN 158
+#define YYLEX_TOKEN_STR (ag_text_strtable+9358)
+#define YYLEX_TOKEN_STR_LEN 12
+#define YYLEX_UNENDING_QUOTE (ag_text_strtable+9371)
+#define YYLEX_UNENDING_QUOTE_LEN 32
+#define zNil (ag_text_strtable+9404)
+#define zNil_LEN 0
+extern char const ag_text_strtable[15696];
+
+#define SCHEME_INIT_TEXT_LINENO 506
+
+#endif /* STRINGS_AG_TEXT_H_GUARD */
diff --git a/agen5/agCgi.c b/agen5/agCgi.c
new file mode 100644
index 0000000..641988d
--- /dev/null
+++ b/agen5/agCgi.c
@@ -0,0 +1,172 @@
+
+/**
+ * @file agCgi.c
+ *
+ * Time-stamp: "2012-03-04 19:19:12 bkorb"
+ *
+ * This is a CGI wrapper for AutoGen. It will take POST-method
+ * name-value pairs and emit AutoGen definitions to a spawned
+ * AutoGen process.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef struct {
+ char const* pzName;
+ char* pzValue;
+} tNameMap;
+
+#define ENV_TABLE \
+ _ET_(SERVER_SOFTWARE) \
+ _ET_(SERVER_NAME) \
+ _ET_(GATEWAY_INTERFACE) \
+ _ET_(SERVER_PROTOCOL) \
+ _ET_(SERVER_PORT) \
+ _ET_(REQUEST_METHOD) \
+ _ET_(PATH_INFO) \
+ _ET_(PATH_TRANSLATED) \
+ _ET_(SCRIPT_NAME) \
+ _ET_(QUERY_STRING) \
+ _ET_(REMOTE_HOST) \
+ _ET_(REMOTE_ADDR) \
+ _ET_(AUTH_TYPE) \
+ _ET_(REMOTE_USER) \
+ _ET_(REMOTE_IDENT) \
+ _ET_(CONTENT_TYPE) \
+ _ET_(CONTENT_LENGTH) \
+ _ET_(HTTP_ACCEPT) \
+ _ET_(HTTP_USER_AGENT) \
+ _ET_(HTTP_REFERER)
+
+static tNameMap nameValueMap[] = {
+#define _ET_(n) { #n, NULL },
+ ENV_TABLE
+#undef _ET_
+ { NULL, NULL }
+};
+
+typedef enum {
+#define _ET_(n) n ## _IDX,
+ ENV_TABLE
+#undef _ET_
+ NAME_CT
+} tNameIdx;
+
+#define pzCgiMethod nameValueMap[ REQUEST_METHOD_IDX ].pzValue
+#define pzCgiQuery nameValueMap[ QUERY_STRING_IDX ].pzValue
+#define pzCgiLength nameValueMap[ CONTENT_LENGTH_IDX ].pzValue
+
+/* = = = START-STATIC-FORWARD = = = */
+static char*
+parseInput(char* pzSrc, int len);
+/* = = = END-STATIC-FORWARD = = = */
+
+LOCAL void
+loadCgi(void)
+{
+ /*
+ * Redirect stderr to a file. If it gets used, we must trap it
+ * and emit the content-type: preamble before actually emitting it.
+ * First, tho, do a simple stderr->stdout redirection just in case
+ * we stumble before we're done with this.
+ */
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ (void)fdopen(STDERR_FILENO, "w" FOPEN_BINARY_FLAG);
+ oops_pfx = CGI_ERR_MSG_FMT;
+ {
+ int tmpfd;
+ AGDUPSTR(cgi_stderr, CGI_TEMP_ERR_FILE_STR, "stderr file");
+ tmpfd = mkstemp(cgi_stderr);
+ if (tmpfd < 0)
+ AG_ABEND(aprf(MKSTEMP_FAIL_FMT, cgi_stderr));
+ dup2(tmpfd, STDERR_FILENO);
+ close(tmpfd);
+ }
+
+ /*
+ * Pull the CGI-relevant environment variables. Anything we don't find
+ * gets an empty string default.
+ */
+ {
+ tNameMap* pNM = nameValueMap;
+ tNameIdx ix = (tNameIdx)0;
+
+ do {
+ pNM->pzValue = getenv(pNM->pzName);
+ if (pNM->pzValue == NULL)
+ pNM->pzValue = (char*)zNil;
+ } while (pNM++, ++ix < NAME_CT);
+ }
+
+ base_ctx = (scan_ctx_t*)AGALOC(sizeof(scan_ctx_t), "CGI ctx");
+ memset((void*)base_ctx, 0, sizeof(scan_ctx_t));
+
+ {
+ size_t textLen = strtoul(pzCgiLength, NULL, 0);
+ char* pzText;
+
+ if (strcasecmp(pzCgiMethod, "POST") == 0) {
+ if (textLen == 0)
+ AG_ABEND(LOAD_CGI_NO_DATA_MSG);
+
+ pzText = AGALOC(textLen + 1, "CGI POST");
+ if (fread(pzText, (size_t)1, textLen, stdin) != textLen)
+ AG_CANT(LOAD_CGI_READ_NAME, LOAD_CGI_READ_WHAT);
+
+ pzText[ textLen ] = NUL;
+
+ base_ctx->scx_data = parseInput(pzText, (int)textLen);
+ AGFREE(pzText);
+
+ } else if (strcasecmp(pzCgiMethod, LOAD_CGI_GET_NAME) == 0) {
+ if (textLen == 0)
+ textLen = strlen(pzCgiQuery);
+ base_ctx->scx_data = parseInput(pzCgiQuery, (int)textLen);
+
+ } else {
+ AG_ABEND(aprf(LOAD_CGI_INVAL_REQ_FMT, pzCgiMethod));
+ /* NOTREACHED */
+#ifdef WARNING_WATCH
+ pzText = NULL;
+#endif
+ }
+ }
+
+ base_ctx->scx_line = 1;
+ base_ctx->scx_fname = LOAD_CGI_DEFS_MARKER;
+ base_ctx->scx_scan = base_ctx->scx_data;
+}
+
+
+static char*
+parseInput(char* pzSrc, int len)
+{
+# define defLen (sizeof("Autogen Definitions cgi;\n") - 1)
+ char* pzRes = AGALOC((len * 2) + defLen + 1, "CGI Definitions");
+
+ memcpy(pzRes, PARSE_INPUT_AG_DEF_STR, defLen);
+ (void)cgi_run_fsm(pzSrc, len, pzRes + defLen, len*2);
+ return AGREALOC(pzRes, strlen(pzRes)+1, "CGI input");
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/agCgi.c */
diff --git a/agen5/agDep.c b/agen5/agDep.c
new file mode 100644
index 0000000..06665d4
--- /dev/null
+++ b/agen5/agDep.c
@@ -0,0 +1,447 @@
+
+/**
+ * @file tpDep.c
+ *
+ * Time-stamp: "2012-04-07 09:01:06 bkorb"
+ *
+ * This module will load a template and return a template structure.
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef struct flist flist_t;
+struct flist {
+ flist_t * next;
+ char fname[1];
+};
+
+static flist_t * src_flist = NULL;
+static flist_t * targ_flist = NULL;
+
+/**
+ * Add a source file to the dependency list
+ *
+ * @param pz pointer to file name
+ */
+LOCAL void
+add_source_file(char const * pz)
+{
+ flist_t ** lp;
+
+ /*
+ * If a source is also a target, then we've created it.
+ * Do not list in source dependencies.
+ */
+ lp = &targ_flist;
+ while (*lp != NULL) {
+ if (strcmp(pz, (*lp)->fname) == 0)
+ return;
+ lp = &((*lp)->next);
+ }
+
+ /*
+ * No check for duplicate in source list. Add if not found.
+ */
+ lp = &src_flist;
+ while (*lp != NULL) {
+ if (strcmp(pz, (*lp)->fname) == 0)
+ return;
+ lp = &((*lp)->next);
+ }
+
+ {
+ size_t l = strlen(pz);
+ flist_t * p = AGALOC(sizeof(*p) + l, "sfile");
+ *lp = p;
+ p->next = NULL;
+ memcpy(p->fname, pz, l + 1);
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fprintf(trace_fp, TRACE_ADD_SRC_FILE_FMT, p->fname);
+ }
+}
+
+/**
+ * remove a source file from the dependency list
+ *
+ * @param pz pointer to file name
+ */
+LOCAL void
+rm_source_file(char const * pz)
+{
+ flist_t ** pp = &src_flist; //!< point to where to stash removed "next"
+ flist_t ** lp = &src_flist; //!< list scanning pointer
+
+ for (;;) {
+ if (*lp == NULL)
+ return;
+ if (strcmp(pz, (*lp)->fname) == 0)
+ break;
+ pp = lp;
+ lp = &((*lp)->next);
+ }
+ {
+ flist_t * p = *lp;
+ *pp = p->next;
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fprintf(trace_fp, TRACE_RM_SRC_FILE_FMT, p->fname);
+ AGFREE(p);
+ }
+}
+
+/**
+ * Add a target file to the dependency list. Avoid files in temp directories.
+ *
+ * @param pz pointer to file name
+ */
+LOCAL void
+add_target_file(char const * pz)
+{
+ flist_t ** lp;
+
+ /*
+ * Skip anything stashed in the temp directory.
+ */
+ if ( (temp_tpl_dir_len > 0)
+ && (strncmp(pz, pz_temp_tpl, temp_tpl_dir_len) == 0)
+ && (pz[temp_tpl_dir_len] == NUL))
+ return;
+
+ /*
+ * Target files override sources, just in case.
+ * (We sometimes extract from files we are about to replace.)
+ */
+ rm_source_file(pz);
+
+ /*
+ * avoid duplicates and add to end of list
+ */
+ lp = &targ_flist;
+ while (*lp != NULL) {
+ if (strcmp(pz, (*lp)->fname) == 0)
+ return;
+ lp = &((*lp)->next);
+ }
+
+ {
+ size_t l = strlen(pz);
+ flist_t * p = AGALOC(sizeof(*p) + l, "tfile");
+ *lp = p;
+ p->next = NULL;
+ memcpy(p->fname, pz, l + 1);
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fprintf(trace_fp, TRACE_ADD_TARG_FILE_FMT, p->fname);
+ }
+}
+
+/**
+ * Remove a target file from the dependency list
+ *
+ * @param pz pointer to file name
+ */
+LOCAL void
+rm_target_file(char const * pz)
+{
+ flist_t ** lp = &targ_flist; //!< list scanning pointer
+
+ for (;;) {
+ if (*lp == NULL)
+ return;
+ if (strcmp(pz, (*lp)->fname) == 0)
+ break;
+ lp = &((*lp)->next);
+ }
+ {
+ flist_t * p = *lp;
+ *lp = p->next;
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fprintf(trace_fp, TRACE_RM_TARG_FILE_FMT, p->fname);
+ AGFREE(p);
+ }
+}
+
+/**
+ * Create a dependency output file
+ */
+LOCAL void
+start_dep_file(void)
+{
+
+ /*
+ * Set dep_file to a temporary file name
+ */
+ {
+ char * tfile_name;
+ size_t dep_name_len;
+
+ if (dep_file == NULL) {
+ dep_name_len = strlen(OPT_ARG(BASE_NAME));
+ tfile_name = AGALOC(dep_name_len + TEMP_SUFFIX_LEN + 1, "dfileb");
+ memcpy(tfile_name, OPT_ARG(BASE_NAME), dep_name_len);
+ memcpy(tfile_name + dep_name_len, TEMP_SUFFIX,
+ TEMP_SUFFIX_LEN + 1);
+
+ } else {
+ dep_name_len = strlen(dep_file);
+ tfile_name = AGALOC(dep_name_len + TEMP_SUFFIX_LEN, "dfile");
+ memcpy(tfile_name, dep_file, dep_name_len);
+ memcpy(tfile_name + dep_name_len, TEMP_SUFFIX + 2,
+ TEMP_SUFFIX_LEN - 1);
+ }
+
+ if (dep_target == NULL) {
+ /*
+ * If there is no target name, then the target is our output file.
+ */
+ char * q = AGALOC(dep_name_len + 1, "t-name");
+ dep_target = q;
+ memcpy(q, tfile_name, dep_name_len);
+ q[dep_name_len] = NUL;
+ }
+
+ mkstemp(tfile_name);
+ dep_file = tfile_name;
+ }
+
+ /*
+ * Create the file and write the leader.
+ */
+ dep_fp = fopen(dep_file, "w");
+
+ if (dep_fp == NULL)
+ AG_CANT(START_DEP_FOPEN_MSG, dep_file);
+
+ fprintf(dep_fp, START_DEP_FILE_FMT, autogenOptions.pzProgPath);
+
+ {
+ int ac = autogenOptions.origArgCt - 1;
+ char ** av = autogenOptions.origArgVect + 1;
+
+ for (;;) {
+ char * arg = *(av++);
+ fprintf(dep_fp, START_DEP_ARG_FMT, arg);
+ if (--ac == 0) break;
+ fputs(DEP_FILE_SPLICE_STR, dep_fp);
+ }
+ fputs("\n", dep_fp);
+ }
+
+ {
+ char const * pnm = autogenOptions.pzPROGNAME;
+ char const * bnm = strchr(dep_target, '/');
+ char * pz;
+
+ if (bnm != NULL)
+ bnm++;
+ else
+ bnm = dep_target;
+
+ {
+ size_t sz = strlen(pnm) + strlen(bnm) + 2; // underscore + NUL
+
+ pz_targ_base = pz = AGALOC(sz, "t list");
+ sprintf(pz, DEP_FILE_TARG_FMT, pnm, bnm);
+ }
+
+ /*
+ * Now scan over the characters in "pz_targ_base". Anything that
+ * is not a legal name character gets replaced with an underscore.
+ */
+ for (;;) {
+ unsigned int ch = (unsigned int)*(pz++);
+ if (ch == NUL)
+ break;
+ if (! IS_ALPHANUMERIC_CHAR(ch))
+ pz[-1] = '_';
+ }
+ }
+}
+
+/**
+ * Set modification time and rename into result file name.
+ */
+static void
+tidy_dep_file(void)
+{
+ static mode_t const fil_mode =
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+
+ /*
+ * Trim off the temporary suffix and rename the dependency file into
+ * place. We used a mkstemp name in case autogen failed.
+ */
+ do {
+ char * pze = strrchr(dep_file, '-');
+ char * pzn;
+ size_t len;
+
+ if (pze == NULL) break;
+
+ len = (pze - dep_file);
+ pzn = AGALOC(len + 1, "dep file");
+ memcpy(pzn, dep_file, len);
+ pzn[len] = NUL;
+
+ unlink(pzn);
+ rename(dep_file, pzn);
+ AGFREE(dep_file);
+ dep_file = pzn;
+ } while (false);
+
+#ifdef HAVE_FCHMOD
+ fchmod(fileno(dep_fp), fil_mode);
+ fclose(dep_fp);
+#else
+ fclose(dep_fp);
+ chmod(dep_file, fil_mode);
+#endif
+ dep_fp = NULL;
+
+ {
+ struct utimbuf tbuf = {
+ .actime = time(NULL),
+ .modtime = start_time
+ };
+
+ utime(dep_file, &tbuf);
+
+ /*
+ * If the target is not the dependency file, then ensure that the
+ * file exists and set its time to the same time. Ignore all errors.
+ */
+ if (strcmp(dep_file, dep_target) != 0) {
+ if (access(dep_target, R_OK) != 0)
+ close( open(dep_target, O_CREAT, fil_mode));
+
+ utime(dep_target, &tbuf);
+ AGFREE(dep_target);
+ }
+ }
+
+ AGFREE(dep_file);
+}
+
+/**
+ * Print out and free a list of files.
+ */
+static void
+print_list(flist_t * flist, char const * TMPDIR, size_t tmpdir_len)
+{
+ /*
+ * Omit temporary sources. They are identified several ways:
+ * 1. the file must be accessible
+ * 2. the file must not match our temporary file template
+ * 3. the file must not match TMPDIR from the environment
+ */
+ while (flist != NULL) {
+ flist_t * p = flist;
+
+ do {
+ if (access(p->fname, R_OK) != 0)
+ break; // no longer accessible
+
+ if ( (temp_tpl_dir_len > 0)
+ && (strncmp(pz_temp_tpl, p->fname, temp_tpl_dir_len) == 0)
+ && (p->fname[temp_tpl_dir_len] == DIRCH))
+ break; // autogen temp file
+
+ if ( (strncmp(TMPDIR, p->fname, tmpdir_len) == 0)
+ && (p->fname[tmpdir_len] == DIRCH) )
+ break; // TMPDIR directory file
+
+ fprintf(dep_fp, DEP_List, p->fname);
+ } while (false);
+
+ flist = p->next;
+ AGFREE(p);
+ }
+}
+
+/**
+ * Finish off the dependency file. Write out the lists of files,
+ * a rule to fulfill make's needs and, optionally, clean up rules.
+ * then close the file and tidy up.
+ */
+LOCAL void
+wrap_up_depends(void)
+{
+ char const * TMPDIR = getenv("TMPDIR");
+ size_t tmpdir_len;
+ if (TMPDIR != NULL) {
+ tmpdir_len = strlen(TMPDIR);
+ } else {
+ TMPDIR = "/tmp";
+ tmpdir_len = 4;
+ }
+
+ fprintf(dep_fp, DEP_TList, pz_targ_base);
+ print_list(targ_flist, TMPDIR, tmpdir_len);
+
+ fprintf(dep_fp, DEP_SList, pz_targ_base);
+ print_list(src_flist, TMPDIR, tmpdir_len);
+
+ targ_flist = src_flist = NULL;
+ fprintf(dep_fp, DEP_FILE_WRAP_FMT, pz_targ_base, dep_target);
+
+ if (dep_phonies) {
+ /*
+ * Remove the target file name IFF it is different from
+ * the dependency file name. The dependency file will not be
+ * removed, but it will be sent waaay back in time.
+ */
+ char * p, *q;
+ AGDUPSTR(p, dep_file, "xx");
+
+ q = p + strlen(p) - (TEMP_SUFFIX_LEN - 2);
+ if ((q > p) && (*q == '-'))
+ *q = NUL;
+ q = p;
+
+ /* DO NOT REMOVE DEPENDENCY FILE */
+ if (strcmp(dep_target, p) == 0)
+ p = (char *)zNil;
+ fprintf(dep_fp, DEP_FILE_CLEAN_FMT, dep_target, pz_targ_base, p);
+ AGFREE(q);
+ }
+
+#if 0
+ if (serv_id != NULLPROCESS) {
+ char * pz = shell_cmd("echo ${AG_Dep_File}");
+ if (*pz != NUL) {
+ /*
+ * The target we are crating will now depend upon the target
+ * created by the spawned autogen run. That spawned run script
+ * is responsible for ensuring that if there are multiple targets,
+ * then they are all chained together so we only worry about one.
+ */
+ static char const incfmt[] =
+ "\n%s : %s\ninclude %s\n";
+ static char const targ[] = ".targ";
+ size_t ln = strlen(pz);
+ char * pt = AGALOC(ln + sizeof(targ), targ);
+ if (strcmp(pz + ln - 4, ".dep") == 0)
+ ln -= 4;
+ memcpy(pt, pz, ln);
+ memcpy(pt + ln, targ, sizeof(targ));
+ fprintf(dep_fp, incfmt, dep_target, pt, pz);
+ AGFREE(pt);
+ }
+
+ AGFREE(pz);
+ }
+#endif
+ tidy_dep_file();
+}
diff --git a/agen5/agInit.c b/agen5/agInit.c
new file mode 100644
index 0000000..ac322bc
--- /dev/null
+++ b/agen5/agInit.c
@@ -0,0 +1,684 @@
+
+/**
+ * @file agInit.c
+ *
+ * Do all the initialization stuff. For daemon mode, only
+ * children will return.
+ *
+ * Time-stamp: "2012-04-14 08:54:02 bkorb"
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static char const *
+make_quote_str(char const * str);
+
+static void
+dep_usage(char const * fmt, ...);
+
+static void
+add_sys_env(char * env_name);
+
+static void
+add_env_vars(void);
+/* = = = END-STATIC-FORWARD = = = */
+
+#ifdef DAEMON_ENABLED
+ static bool evalProto(char const ** ppzS, uint16_t* pProto);
+ static void spawnPipe(char const* pzFile);
+ static void spawnListens(char const * pzPort, sa_family_t af);
+ static void daemonize(char const *, char const *, char const *,
+ char const *);
+#endif
+
+#include "expr.ini"
+
+/**
+ * Various initializations.
+ *
+ * @param arg_ct the count of program arguments, plus 1.
+ * @param arg_vec the program name plus its arguments
+ */
+LOCAL void
+initialize(int arg_ct, char** arg_vec)
+{
+ ag_scribble_init();
+
+ /*
+ * Initialize all the Scheme functions.
+ */
+ ag_init();
+ last_scm_cmd = SCHEME_INIT_TEXT;
+ ag_scm_c_eval_string_from_file_line(
+ SCHEME_INIT_TEXT, AG_TEXT_STRTABLE_FILE, SCHEME_INIT_TEXT_LINENO);
+
+ {
+#if GUILE_VERSION > 200000
+ static char const * const module = SCHEME_INIT_DEBUG_2_0;
+#else
+ static char const * const module = SCHEME_INIT_DEBUG_1_6;
+#endif
+ char * p = aprf(INIT_SCM_ERRS_FMT, module);
+ last_scm_cmd = p;
+ ag_scm_c_eval_string_from_file_line(p, __FILE__, __LINE__);
+ AGFREE(p);
+ }
+
+ last_scm_cmd = NULL;
+ processing_state = PROC_STATE_OPTIONS;
+ add_env_vars();
+
+ process_ag_opts(arg_ct, arg_vec);
+ exit_code = AUTOGEN_EXIT_LOAD_ERROR;
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ SCM_EVAL_CONST(INIT_SCM_DEBUG_FMT);
+
+#ifdef DAEMON_ENABLED
+
+ if (! HAVE_OPT(DAEMON))
+ return;
+
+#ifdef DEBUG_ENABLED
+ {
+ static char const logf[] = "/tmp/ag-debug.txt";
+ daemonize("/", logf, logf, logf);
+ }
+#else
+ daemonize("/", DEV_NULL, DEV_NULL, DEV_NULL);
+#endif /* DEBUG_ENABLED */
+
+ {
+ sa_family_t af = AF_INET;
+ char const * pzS = OPT_ARG(DAEMON);
+
+ if (evalProto(&pzS, &af))
+ spawnListens(pzS, af);
+ else
+ spawnPipe(pzS);
+ }
+#endif /* DAEMON_ENABLED */
+}
+
+/**
+ * make a name resilient to machinations made by 'make'.
+ * Basically, dollar sign characters are doubled.
+ *
+ * @param str the input string
+ * @returns a newly allocated string with the '$' characters doubled
+ */
+static char const *
+make_quote_str(char const * str)
+{
+ size_t sz = strlen(str) + 1;
+ char const * scan = str;
+ char * res;
+
+ for (;;) {
+ char * p = strchr(scan, '$');
+ if (p == NULL)
+ break;
+ sz++;
+ scan = scan + 1;
+ }
+
+ res = AGALOC(sz, "q name");
+ scan = res;
+
+ for (;;) {
+ char * p = strchr(str, '$');
+
+ if (p == NULL)
+ break;
+ sz = (p - str) + 1;
+ memcpy(res, str, sz);
+ res += sz;
+ str += sz;
+ *(res++) = '$';
+ }
+
+ strcpy(res, str);
+ return scan;
+}
+
+/**
+ * Error in dependency specification
+ *
+ * @param fmt the error message format
+ */
+static void
+dep_usage(char const * fmt, ...)
+{
+ char * msg;
+
+ {
+ va_list ap;
+ va_start(ap, fmt);
+ (void)vasprintf(&msg, fmt, ap);
+ va_end(ap);
+ }
+
+ usage_message(USAGE_INVAL_DEP_OPT_FMT, msg);
+ /* NOTREACHED */
+}
+
+/**
+ * Configure a dependency option.
+ * Handles any of these letters: MFQTPGD as the first part of the option
+ * argument.
+ *
+ * @param opts the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+LOCAL void
+config_dep(tOptions * opts, tOptDesc * od)
+{
+ char const * opt_arg = od->optArg.argString;
+ (void)opts;
+
+ /*
+ * The option argument is optional. Make sure we have one.
+ */
+ if (opt_arg == NULL)
+ return;
+
+ while (*opt_arg == 'M') opt_arg++;
+ opt_arg = SPN_WHITESPACE_CHARS(opt_arg);
+
+ switch (*opt_arg) {
+ case 'F':
+ if (dep_file != NULL)
+ dep_usage(CFGDEP_DUP_TARGET_MSG);
+
+ opt_arg = SPN_WHITESPACE_CHARS(opt_arg + 1);
+ AGDUPSTR(dep_file, opt_arg, "f name");
+ break;
+
+ case 'Q':
+ case 'T':
+ {
+ bool quote_it = (*opt_arg == 'Q');
+
+ if (dep_target != NULL)
+ dep_usage(CFGDEP_DUP_TARGET_MSG);
+
+ opt_arg = SPN_WHITESPACE_CHARS(opt_arg + 1);
+ if (quote_it)
+ dep_target = make_quote_str(opt_arg);
+ else
+ AGDUPSTR(dep_target, opt_arg, "t name");
+ break;
+ }
+
+ case 'P':
+ dep_phonies = true;
+ break;
+
+ case 'D':
+ case 'G':
+ case NUL:
+ /*
+ * 'D' and 'G' make sense to GCC, not us. Ignore 'em. If we
+ * found a NUL byte, then act like we found -MM on the command line.
+ */
+ break;
+
+ default:
+ dep_usage(CFGDEP_UNKNOWN_DEP_FMT, opt_arg);
+ }
+}
+
+/**
+ * Add a system name to the environment. The input name is up-cased and
+ * made to conform to environment variable names. If not already in the
+ * environment, it is added with the string value "1".
+ *
+ * @param env_name in/out: system name to export
+ */
+static void
+add_sys_env(char * env_name)
+{
+ int i = 2;
+
+ for (;;) {
+ if (IS_UPPER_CASE_CHAR(env_name[i]))
+ env_name[i] = tolower(env_name[i]);
+ else if (! IS_ALPHANUMERIC_CHAR(env_name[i]))
+ env_name[i] = '_';
+
+ if (env_name[ ++i ] == NUL)
+ break;
+ }
+
+ /*
+ * If the user already has something in the environment, do not
+ * override it.
+ */
+ if (getenv(env_name) == NULL) {
+ char* pz;
+
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, TRACE_ADD_TO_ENV_FMT, env_name);
+
+ pz = aprf(ADD_SYS_ENV_VAL_FMT, env_name);
+ putenv(pz);
+ }
+}
+
+/**
+ * Define system environment variables. "__autogen__=1" is exported,
+ * along with various ones derivable from Solaris sysinfo(3p) or uname.
+ */
+static void
+add_env_vars(void)
+{
+ /*
+ * Set the last resort search directories first (lowest priority)
+ * The lowest of the low is the config time install data dir.
+ * Next is the *current* directory of this executable.
+ */
+ SET_OPT_TEMPL_DIRS(DFT_TPL_DIR_DATA);
+ SET_OPT_TEMPL_DIRS(DFT_TPL_DIR_RELATIVE);
+
+ {
+ char z[ SCRIBBLE_SIZE ] = "__autogen__";
+#if defined(HAVE_SOLARIS_SYSINFO)
+ static const int nm[] = {
+ SI_SYSNAME, SI_HOSTNAME, SI_ARCHITECTURE, SI_HW_PROVIDER,
+#ifdef SI_PLATFORM
+ SI_PLATFORM,
+#endif
+ SI_MACHINE };
+ int ix;
+ long sz;
+
+ add_sys_env(z);
+ for (ix = 0; ix < sizeof(nm)/sizeof(nm[0]); ix++) {
+ sz = sysinfo(nm[ix], z+2, sizeof(z) - 2);
+ if (sz > 0) {
+ sz += 2;
+ while (z[sz-1] == NUL) sz--;
+ strcpy(z + sz, ADD_ENV_VARS_SUFFIX_FMT+2);
+ add_sys_env(z);
+ }
+ }
+
+#elif defined(HAVE_UNAME_SYSCALL)
+ struct utsname unm;
+
+ add_sys_env(z);
+ if (uname(&unm) != 0)
+ AG_CANT(UNAME_CALL_NAME, SYSCALL_NAME);
+
+ sprintf(z+2, ADD_ENV_VARS_SUFFIX_FMT, unm.sysname);
+ add_sys_env(z);
+
+ sprintf(z+2, ADD_ENV_VARS_SUFFIX_FMT, unm.machine);
+ add_sys_env(z);
+
+ sprintf(z+2, ADD_ENV_VARS_SUFFIX_FMT, unm.nodename);
+ add_sys_env(z);
+#else
+
+ add_sys_env(z);
+#endif
+ }
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * T H E F O L L O W I N G I S D E A D C O D E
+ *
+ * Someday, I want to enable daemon code, but need lotsa time.....
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#ifdef DAEMON_ENABLED
+
+ static bool
+evalProto(char const ** ppzS, uint16_t* pProto)
+{
+ char const * pzS = *ppzS;
+
+ if (IS_ALPHABETIC_CHAR(*pzS)) {
+ inet_family_map_t* pMap = inet_family_map;
+ do {
+ if (strncmp(pzS, pMap->pz_name, pMap->nm_len) == 0) {
+ *pProto = pMap->family;
+ *ppzS += pMap->nm_len;
+ return true;
+ }
+ } while ((++pMap)->pz_name != NULL);
+ }
+
+ return IS_DEC_DIGIT_CHAR(*pzS);
+}
+
+ LOCAL void
+handleSighup(int sig)
+{
+ redoOptions = true;
+}
+
+ static void
+spawnPipe(char const * pzFile)
+{
+# define S_IRW_ALL \
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
+ fd_pair_t fdpair;
+ char* pzIn;
+ char* pzOut;
+
+ {
+ size_t len = 2 * (strlen(pzFile) + 5);
+ pzIn = AGALOC(len + 5, "fifo name");
+ pzOut = pzIn + sprintf(pzIn, PIPE_FIFO_IN_NAME_FMT, pzFile) + 1;
+ }
+
+ unlink(pzIn);
+ if ((mkfifo(pzIn, S_IRW_ALL) != 0) && (errno != EEXIST))
+ AG_CANT(PIPE_MKFIFO_NAME, pzIn);
+
+ (void)sprintf(pzOut, PIPE_FIFO_OUT_NAME_FMT, pzFile);
+ unlink(pzOut);
+ if ((mkfifo(pzOut, S_IRW_ALL) != 0) && (errno != EEXIST))
+ AG_CANT(PIPE_MKFIFO_NAME, pzOut);
+
+ fdpair.fd_read = open(pzIn, O_RDONLY);
+ if (fdpair.fd_read < 0)
+ AG_CANT(PIPE_FIFO_OPEN, pzIn);
+
+ {
+ struct pollfd polls[1];
+ polls[0].fd = fdpair.fd_read;
+ polls[0].events = POLLIN | POLLPRI;
+
+ for (;;) {
+ int ct = poll(polls, 1, -1);
+ struct strrecvfd recvfd;
+ pid_t child;
+
+ switch (ct) {
+ case -1:
+ if ((errno != EINTR) || (! redoOptions))
+ goto spawnpipe_finish;
+
+ optionRestore(&autogenOptions);
+ process_ag_opts(autogenOptions.origArgCt,
+ autogenOptions.origArgVect);
+ SET_OPT_DEFINITIONS(PIPE_DEFS_STDIN_STR);
+ break;
+
+ case 1:
+ if ((polls[0].revents & POLLIN) == 0)
+ continue;
+
+ child = fork();
+ switch (child) {
+ default:
+ waitpid(child, &ct, 0);
+ continue;
+
+ case -1:
+ AG_CANT(PIPE_FORK_NAME, zNil);
+
+ case 0:
+ }
+
+ if (dup2(fdpair.fd_read, STDIN_FILENO) != STDIN_FILENO)
+ AG_CANT(PIPE_DUP2_NAME_STR, PIPE_DEFS_STDIN_NAME);
+
+ fdpair.fd_write = open(pzOut, O_WRONLY);
+ if (fdpair.fd_write < 0)
+ AG_CANT(PIPE_FIFO_OPEN, pzOut);
+
+ polls[0].fd = fdpair.fd_write;
+ polls[0].events = POLLOUT;
+ if (poll(polls, 1, -1) != 1)
+ AG_CANT(PIPE_POLL_NAME_STR, PIPE_WRITE_NAME_STR);
+
+ if (dup2(fdpair.fd_write, STDOUT_FILENO) != STDOUT_FILENO)
+ AG_CANT(PIPE_DUP2_NAME_STR, pzOut);
+
+ return;
+ }
+ }
+ }
+
+ spawnpipe_finish:
+ unlink(pzIn);
+ unlink(pzOut);
+ AGFREE(pzIn);
+
+# undef S_IRW_ALL
+
+ exit(EXIT_SUCCESS);
+}
+
+
+ static void
+spawnListens(char const * pzPort, sa_family_t addr_family)
+{
+ int socket_fd = socket(addr_family, SOCK_STREAM, 0);
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in in_addr;
+ struct sockaddr_un un_addr;
+ } sa;
+
+ uint32_t addr_len;
+
+ if (socket_fd < 0)
+ AG_CANT("socket", "AF_INET/SOCK_STREAM");
+
+ switch (addr_family) {
+
+ case AF_UNIX:
+ {
+ uint32_t p_len = strlen(pzPort);
+
+ if (p_len > sizeof(sa.un_addr.sun_path))
+ AG_ABEND(aprf("AF_UNIX path exceeds %d", p_len));
+ sa.un_addr.sun_family = AF_UNIX;
+ strncpy(sa.un_addr.sun_path, pzPort, p_len);
+ addr_len = sizeof(sa.un_addr) - sizeof(sa.un_addr.sun_path) + p_len;
+ }
+ break;
+
+ case AF_INET:
+ {
+ uint16_t port;
+ char* pz;
+
+ sa.in_addr.sin_family = AF_INET;
+ sa.in_addr.sin_addr.s_addr = INADDR_ANY;
+
+ errno = 0;
+ if ((unlink(pzPort) != 0) && (errno != ENOENT))
+ AG_CANT("unlink", pzPort);
+
+ port = (uint16_t)strtol(pzPort, &pz, 0);
+ if ((errno != 0) || (*pz != NUL))
+ AG_ABEND(aprf("Invalid port number: '%s'", pzPort));
+
+ sa.in_addr.sin_port = htons((short)port);
+ addr_len = sizeof(sa.in_addr);
+ }
+ break;
+
+ default:
+ AG_ABEND(aprf("The '%d' address family cannot be handled",
+ addr_family));
+ }
+
+ if (bind(socket_fd, &sa.addr, addr_len) < 0) {
+ char* pz = aprf(LISTEN_PORT_FMT, pzPort, addr_family);
+ AG_CANT("bind", pz);
+ }
+
+ if (fcntl(socket_fd, F_SETFL, O_NONBLOCK) < 0)
+ AG_CANT("socket-fcntl", "FNDELAY");
+
+ if (listen(socket_fd, 5) < 0)
+ AG_CANT("listen", aprf(LISTEN_PORT_FMT, pzPort));
+
+ for (;;) {
+ fd_set fds;
+ int max_fd = socket_fd;
+ int new_conns;
+
+ FD_ZERO(&fds);
+ FD_SET(socket_fd, &fds);
+
+ new_conns = select(max_fd, &fds, NULL, NULL, NULL);
+ if (new_conns < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (! redoOptions) {
+ unlink(pzPort);
+ exit(EXIT_SUCCESS);
+ }
+
+ optionRestore(&autogenOptions);
+ process_ag_opts(autogenOptions.origArgCt,
+ autogenOptions.origArgVect);
+ SET_OPT_DEFINITIONS("-");
+
+ continue;
+ }
+
+ if (new_conns > 0) {
+ switch (fork()) {
+ default: continue;
+ case -1:
+ AG_CANT("fork", zNil);
+
+ case 0: break;
+ }
+ break;
+ }
+ }
+
+ for (;;) {
+ static int try_ct = 0;
+ struct sockaddr addr;
+ socklen_t addr_len;
+ int fd = accept(socket_fd, &addr, &addr_len);
+ if (fd < 0)
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if (EAGAIN != EWOULDBLOCK)
+ case EWOULDBLOCK:
+#endif
+ if (try_ct++ < 10000) {
+ sleep(1);
+ continue;
+ }
+ }
+ socket_fd = fd;
+ break;
+ }
+
+ if (dup2(socket_fd, STDOUT_FILENO) != STDOUT_FILENO)
+ AG_CANT("dup2", "out on socket_fd");
+ if (dup2(socket_fd, STDIN_FILENO) != STDIN_FILENO)
+ AG_CANT("dup2", "in on socket_fd");
+}
+
+
+ static void
+daemonize(char const * pzStdin, char const * pzStdout, char const * pzStderr,
+ char const * pzDaemonDir)
+{
+ static char const zNoFork[] = "Error %d while forking: %s\n";
+ /*
+ * Become a daemon process by exiting the current process
+ * and allowing the child to continue. Also, change stdin,
+ * stdout and stderr to point to /dev/null and change to
+ * the root directory ('/').
+ */
+ {
+ int ret = fork();
+
+ switch (ret) {
+ case -1:
+ fprintf(stderr, zNoFork, errno, strerror(errno));
+ default:
+ exit(ret);
+
+ case 0:
+ break;
+ }
+ }
+
+ /*
+ * Now, become a process group and session group leader.
+ */
+ if (setsid() == -1) {
+ fprintf(stderr, "Error %d setting session ID: %s\n",
+ errno, strerror(errno));
+ exit(99);
+ }
+
+ /*
+ * There is now no controlling terminal. However, if we open anything
+ * that resembles a terminal, it will become our controlling terminal.
+ * So, we will fork again and the child will not be a group leader and
+ * thus cannot gain a controlling terminal.
+ */
+ switch (fork()) {
+ case -1:
+ fprintf(stderr, zNoFork, errno, strerror(errno));
+ exit(99);
+
+ case 0:
+ break;
+
+ default:
+ exit(EXIT_SUCCESS); /* parent process - silently go away */
+ }
+
+ umask(0);
+ if (pzDaemonDir == (char*)NULL)
+ pzDaemonDir = "/";
+
+ chdir(pzDaemonDir);
+
+ /*
+ * Reopen the input, output and error files, unless we were told not to
+ */
+ if (pzStdin != (char*)NULL)
+ freopen(pzStdin, "r", stdin);
+
+ if (pzStdout != (char*)NULL)
+ freopen(pzStdout, "w", stdout);
+
+ if (pzStderr != (char*)NULL)
+ freopen(pzStderr, "w", stderr);
+
+ /* We are a daemon now */
+}
+#endif /* DAEMON_ENABLED */
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/agInit.c */
diff --git a/agen5/agShell.c b/agen5/agShell.c
new file mode 100644
index 0000000..80bcbdd
--- /dev/null
+++ b/agen5/agShell.c
@@ -0,0 +1,695 @@
+/**
+ * @file agShell.c
+ *
+ * Time-stamp: "2012-03-04 19:08:09 bkorb"
+ *
+ * Manage a server shell process
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+static char * cur_dir = NULL;
+
+/*=gfunc chdir
+ *
+ * what: Change current directory
+ *
+ * exparg: dir, new directory name
+ *
+ * doc: Sets the current directory for AutoGen. Shell commands will run
+ * from this directory as well. This is a wrapper around the Guile
+ * native function. It returns its directory name argument and
+ * fails the program on failure.
+=*/
+SCM
+ag_scm_chdir(SCM dir)
+{
+ static char const zChdirDir[] = "chdir directory";
+
+ scm_chdir(dir);
+
+ /*
+ * We're still here, so we have a valid argument.
+ */
+ if (cur_dir != NULL)
+ free(cur_dir);
+ {
+ char const * pz = ag_scm2zchars(dir, zChdirDir);
+ cur_dir = malloc(AG_SCM_STRLEN(dir) + 1);
+ strcpy((char*)cur_dir, pz);
+ }
+ return dir;
+}
+
+/*=gfunc shell
+ *
+ * what: invoke a shell script
+ * general_use:
+ *
+ * exparg: command, shell command - the result value is the stdout output.
+ *
+ * doc:
+ * Generate a string by writing the value to a server shell and reading the
+ * output back in. The template programmer is responsible for ensuring that
+ * it completes within 10 seconds. If it does not, the server will be
+ * killed, the output tossed and a new server started.
+ *
+ * Please note: This is the same server process used by the '#shell'
+ * definitions directive and backquoted @code{`} definitions. There may be
+ * left over state from previous shell expressions and the @code{`}
+ * processing in the declarations. However, a @code{cd} to the original
+ * directory is always issued before the new command is issued.
+ *
+ * Also note: When initializing, autogen will set the environment
+ * variable "AGexe" to the full path of the autogen executable.
+=*/
+SCM
+ag_scm_shell(SCM cmd)
+{
+#ifndef SHELL_ENABLED
+ return cmd;
+#else
+ if (! AG_SCM_STRING_P(cmd))
+ return SCM_UNDEFINED;
+ {
+ char* pz = shell_cmd(ag_scm2zchars(cmd, "command"));
+ cmd = AG_SCM_STR02SCM(pz);
+ AGFREE((void*)pz);
+ return cmd;
+ }
+#endif
+}
+
+/*=gfunc shellf
+ *
+ * what: format a string, run shell
+ * general_use:
+ *
+ * exparg: format, formatting string
+ * exparg: format-arg, list of arguments to formatting string, opt, list
+ *
+ * doc: Format a string using arguments from the alist,
+ * then send the result to the shell for interpretation.
+=*/
+SCM
+ag_scm_shellf(SCM fmt, SCM alist)
+{
+ int len = scm_ilength(alist);
+ char* pz;
+
+#ifdef DEBUG_ENABLED
+ if (len < 0)
+ AG_ABEND(SHELLF_BAD_ALIST_MSG);
+#endif
+
+ pz = ag_scm2zchars(fmt, "format");
+ fmt = run_printf(pz, len, alist);
+
+#ifdef SHELL_ENABLED
+ pz = shell_cmd(ag_scm2zchars(fmt, "shell script"));
+ fmt = AG_SCM_STR02SCM(pz);
+ AGFREE((void*)pz);
+#endif
+ return fmt;
+}
+
+#ifndef SHELL_ENABLED
+HIDE_FN(void closeServer(void) {;})
+
+HIDE_FN(char * shell_cmd(char const* pzCmd)) {
+ char* pz;
+ AGDUPSTR(pz, pzCmd, "dummy shell command");
+ return pz;
+}
+#else
+
+/*
+ * Dual pipe opening of a child process
+ */
+static fp_pair_t serv_pair = { NULL, NULL };
+static pid_t serv_id = NULLPROCESS;
+static bool was_close_err = false;
+static int log_ct = 0;
+static char const * last_cmd = NULL;
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+handle_signal(int signo);
+
+static void
+set_orig_dir(void);
+
+static bool
+send_cmd_ok(char const * cmd);
+
+static void
+start_server_cmd_trace(void);
+
+static void
+send_server_init_cmds(void);
+
+static void
+server_setup(void);
+
+static int
+chain_open(int in_fd, char const ** arg_v, pid_t * child_pid);
+
+static pid_t
+server_open(fd_pair_t * fd_pair, char const ** ppArgs);
+
+static pid_t
+server_fp_open(fp_pair_t * fp_pair, char const ** ppArgs);
+
+static inline void
+realloc_text(char ** p_txt, size_t * p_sz, size_t need_len);
+
+static char*
+load_data(void);
+/* = = = END-STATIC-FORWARD = = = */
+
+LOCAL void
+close_server_shell(void)
+{
+ if (serv_id == NULLPROCESS)
+ return;
+
+ (void)kill(serv_id, SIGTERM);
+#ifdef HAVE_USLEEP
+ usleep(100000); /* 1/10 of a second */
+#else
+ sleep(1);
+#endif
+ (void)kill(serv_id, SIGKILL);
+ serv_id = NULLPROCESS;
+
+ /*
+ * This guard should not be necessary. However, sometimes someone
+ * holds an allocation pthread lock when a seg fault occurrs. fclose
+ * needs that lock, so we hang waiting for it. Oops. So, when we
+ * are aborting, we just let the OS close these file descriptors.
+ */
+ if (processing_state != PROC_STATE_ABORTING) {
+ (void)fclose(serv_pair.fp_read);
+ /*
+ * This is _completely_ wrong, but sometimes there are data left
+ * hanging about that gets sucked up by the _next_ server shell
+ * process. That should never, ever be in any way possible, but
+ * it is the only explanation for a second server shell picking up
+ * the initialization string twice. It must be a broken timing
+ * issue in the Linux stdio code. I have no other explanation.
+ */
+ fflush(serv_pair.fp_write);
+ (void)fclose(serv_pair.fp_write);
+ }
+
+ serv_pair.fp_read = serv_pair.fp_write = NULL;
+}
+
+/**
+ * handle SIGALRM and SIGPIPE signals while waiting for server shell
+ * responses.
+ */
+static void
+handle_signal(int signo)
+{
+ static int timeout_limit = 5;
+ if ((signo == SIGALRM) && (--timeout_limit <= 0))
+ AG_ABEND(TOO_MANY_TIMEOUTS_MSG);
+
+ fprintf(trace_fp, SHELL_DIE_ON_SIGNAL_FMT, strsignal(signo), signo);
+ was_close_err = true;
+
+ (void)fputs(SHELL_LAST_CMD_MSG, trace_fp);
+ {
+ char const* pz = (last_cmd == NULL)
+ ? SHELL_UNK_LAST_CMD_MSG : last_cmd;
+ fprintf(trace_fp, SHELL_CMD_FMT, cur_dir, pz, SH_DONE_MARK, log_ct);
+ }
+ last_cmd = NULL;
+ close_server_shell();
+}
+
+/**
+ * first time starting a server shell, we get our current directory.
+ * That value is kept, but may be changed via a (chdir "...") scheme call.
+ */
+static void
+set_orig_dir(void)
+{
+ char * p = malloc(AG_PATH_MAX);
+ if (p == NULL)
+ AG_ABEND(SET_ORIG_DIR_NO_MEM_MSG);
+
+ cur_dir = getcwd(p, AG_PATH_MAX);
+
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fputs(TRACE_SHELL_FIRST_START, trace_fp);
+}
+
+/**
+ * Send a command string down to the server shell
+ */
+static bool
+send_cmd_ok(char const * cmd)
+{
+ last_cmd = cmd;
+ fprintf(serv_pair.fp_write, SHELL_CMD_FMT, cur_dir, last_cmd,
+ SH_DONE_MARK, ++log_ct);
+
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL) {
+ fprintf(trace_fp, LOG_SEP_FMT, log_ct);
+ fprintf(trace_fp, SHELL_CMD_FMT, cur_dir, last_cmd,
+ SH_DONE_MARK, log_ct);
+ }
+
+ (void)fflush(serv_pair.fp_write);
+ if (was_close_err)
+ fprintf(trace_fp, CMD_FAIL_FMT, cmd);
+ return ! was_close_err;
+}
+
+/**
+ * Tracing level is TRACE_EVERYTHING, so send the server shell
+ * various commands to start "set -x" tracing and display the
+ * trap actions.
+ */
+static void
+start_server_cmd_trace(void)
+{
+ fputs(TRACE_XTRACE_MSG, trace_fp);
+ if (send_cmd_ok(SHELL_XTRACE_CMDS)) {
+ char * pz = load_data();
+ fputs(SHELL_RES_DISCARD_MSG, trace_fp);
+ fprintf(trace_fp, TRACE_TRAP_STATE_FMT, pz);
+ AGFREE((void*)pz);
+ }
+}
+
+/**
+ * Send down the initialization string with our PID in it, as well
+ * as the full path name of the autogen executable.
+ */
+static void
+send_server_init_cmds(void)
+{
+ was_close_err = false;
+
+ {
+ char * pzc = AGALOC(SHELL_INIT_STR_LEN
+ + 11 // log10(1 << 32) + 1
+ + strlen(autogenOptions.pzProgPath),
+ "server init");
+ sprintf(pzc, SHELL_INIT_STR, (unsigned int)getpid(),
+ autogenOptions.pzProgPath,
+ (dep_fp == NULL) ? "" : dep_file);
+
+ if (send_cmd_ok(pzc))
+ AGFREE((void*)load_data());
+ AGFREE(pzc);
+ }
+
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fputs(SHELL_RES_DISCARD_MSG, trace_fp);
+
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING)
+ start_server_cmd_trace();
+}
+
+/**
+ * Perform various initializations required when starting
+ * a new server shell process.
+ */
+static void
+server_setup(void)
+{
+ if (cur_dir == NULL)
+ set_orig_dir();
+ else if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fputs(SHELL_RESTART_MSG, trace_fp);
+
+ {
+ struct sigaction new_sa;
+ new_sa.sa_handler = handle_signal;
+ new_sa.sa_flags = 0;
+ (void)sigemptyset(&new_sa.sa_mask);
+ (void)sigaction(SIGPIPE, &new_sa, NULL);
+ (void)sigaction(SIGALRM, &new_sa, NULL);
+ }
+
+ send_server_init_cmds();
+
+ last_cmd = NULL;
+}
+
+/**
+ * Given an FD for an inferior process to use as stdin,
+ * start that process and return a NEW FD that that process
+ * will use for its stdout. Requires the argument vector
+ * for the new process and, optionally, a pointer to a place
+ * to store the child's process id.
+ *
+ * @param stdinFd the file descriptor for the process' stdin
+ * @param ppArgs The program and argument vector
+ * @param pChild where to stash the child process PID
+ *
+ * @returns the read end of a pipe the child process uses for stdout
+ */
+static int
+chain_open(int in_fd, char const ** arg_v, pid_t * child_pid)
+{
+ fd_pair_t out_pair = { -1, -1 };
+ pid_t ch_pid;
+ char const * shell;
+
+ /*
+ * If we did not get an arg list, use the default
+ */
+ if (arg_v == NULL)
+ arg_v = server_args;
+
+ /*
+ * If the arg list does not have a program,
+ * assume the zShellProg from the environment, or, failing
+ * that, then sh. Set argv[0] to whatever we decided on.
+ */
+ if (shell = *arg_v,
+ (shell == NULL) || (*shell == NUL))
+
+ *arg_v = shell = shell_program;
+
+ /*
+ * Create a pipe it will be the child process' stdout,
+ * and the parent will read from it.
+ */
+ if (pipe((int*)&out_pair) < 0) {
+ if (child_pid != NULL)
+ *child_pid = NOPROCESS;
+ return -1;
+ }
+
+ /*
+ * Make sure our standard streams are all flushed out before forking.
+ * (avoid duplicate output). Call fork() and see which process we become
+ */
+ fflush(stdout);
+ fflush(stderr);
+ if (trace_fp != stderr)
+ fflush(trace_fp);
+
+ ch_pid = fork();
+ switch (ch_pid) {
+ case NOPROCESS: /* parent - error in call */
+ close(in_fd);
+ close(out_pair.fd_read);
+ close(out_pair.fd_write);
+ if (child_pid != NULL)
+ *child_pid = NOPROCESS;
+ return -1;
+
+ default: /* parent - return opposite FD's */
+ if (child_pid != NULL)
+ *child_pid = ch_pid;
+
+ close(in_fd);
+ close(out_pair.fd_write);
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL) {
+ fprintf(trace_fp, TRACE_SHELL_PID_FMT, (unsigned int)ch_pid);
+ fflush(trace_fp);
+ }
+
+ return out_pair.fd_read;
+
+ case NULLPROCESS: /* child - continue processing */
+ break;
+ }
+
+ /*
+ * Close the pipe end handed back to the parent process,
+ * plus stdin and stdout.
+ */
+ close(out_pair.fd_read);
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+
+ /*
+ * Set stdin/out to the fd passed in and the write end of our new pipe.
+ */
+ fcntl(out_pair.fd_write, F_DUPFD, STDOUT_FILENO);
+ fcntl(in_fd, F_DUPFD, STDIN_FILENO);
+
+ /*
+ * set stderr to our trace file (if not stderr).
+ */
+ if (trace_fp != stderr) {
+ close(STDERR_FILENO);
+ fcntl(fileno(trace_fp), F_DUPFD, STDERR_FILENO);
+ }
+
+ /*
+ * Make the output file unbuffered only.
+ * We do not want to wait for shell output buffering.
+ */
+ setvbuf(stdout, NULL, _IONBF, (size_t)0);
+
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL) {
+ fprintf(trace_fp, TRACE_SHELL_STARTS_FMT, shell);
+
+ fflush(trace_fp);
+ }
+
+ execvp((char*)shell, (char**)arg_v);
+ AG_CANT("execvp", shell);
+ /* NOTREACHED */
+ return -1;
+}
+
+/**
+ * Given a pointer to an argument vector, start a process and
+ * place its stdin and stdout file descriptors into an fd pair
+ * structure. The "fd_write" connects to the inferior process
+ * stdin, and the "fd_read" connects to its stdout. The calling
+ * process should write to "fd_write" and read from "fd_read".
+ * The return value is the process id of the created process.
+ */
+static pid_t
+server_open(fd_pair_t * fd_pair, char const ** ppArgs)
+{
+ pid_t chId = NOPROCESS;
+
+ /*
+ * Create a bi-directional pipe. Writes on 0 arrive on 1
+ * and vice versa, so the parent and child processes will
+ * read and write to opposite FD's.
+ */
+ if (pipe((int*)fd_pair) < 0)
+ return NOPROCESS;
+
+ fd_pair->fd_read = chain_open(fd_pair->fd_read, ppArgs, &chId);
+ if (chId == NOPROCESS)
+ close(fd_pair->fd_write);
+
+ return chId;
+}
+
+
+/**
+ * Identical to "server_open()", except that the "fd"'s are "fdopen(3)"-ed
+ * into file pointers instead.
+ */
+static pid_t
+server_fp_open(fp_pair_t * fp_pair, char const ** ppArgs)
+{
+ fd_pair_t fd_pair;
+ pid_t chId = server_open(&fd_pair, ppArgs);
+
+ if (chId == NOPROCESS)
+ return chId;
+
+ fp_pair->fp_read = fdopen(fd_pair.fd_read, "r" FOPEN_BINARY_FLAG);
+ fp_pair->fp_write = fdopen(fd_pair.fd_write, "w" FOPEN_BINARY_FLAG);
+ return chId;
+}
+
+static inline void
+realloc_text(char ** p_txt, size_t * p_sz, size_t need_len)
+{
+ *p_sz = (*p_sz + need_len + 0xFFF) & ~0xFFF;
+ *p_txt = AGREALOC((void*)*p_txt, *p_sz, "expand text");
+}
+
+/**
+ * Read data from a file pointer (a pipe to a process in this context)
+ * until we either get EOF or we get a marker line back.
+ * The read data are stored in a malloc-ed string that is truncated
+ * to size at the end. Input is assumed to be an ASCII string.
+ */
+static char*
+load_data(void)
+{
+ char* text;
+ size_t text_sz = 4096;
+ size_t used_ct = 0;
+ char* scan;
+ char zLine[ 1024 ];
+ int retry_ct = 0;
+#define LOAD_RETRY_LIMIT 4
+
+ scan = text = AGALOC(text_sz, "Text Block");
+ *text = NUL;
+
+ for (;;) {
+ char * line_p;
+
+ /*
+ * Set a timeout so we do not wait forever. Sometimes we don't wait
+ * at all and we should. Retry in those cases (but not on EOF).
+ */
+ alarm((unsigned int)OPT_VALUE_TIMEOUT);
+ line_p = fgets(zLine, (int)sizeof(zLine), serv_pair.fp_read);
+ alarm(0);
+
+ if (line_p == NULL) {
+ /*
+ * Guard against a server timeout
+ */
+ if (serv_id == NULLPROCESS)
+ break;
+
+ if ((OPT_VALUE_TRACE >= TRACE_SERVER_SHELL) || (retry_ct++ > 0))
+ fprintf(trace_fp, SHELL_READ_ERR_FMT, errno, strerror(errno));
+
+ if (feof(serv_pair.fp_read) || (retry_ct > LOAD_RETRY_LIMIT))
+ break;
+
+ continue; /* no data - retry */
+ }
+
+ /*
+ * Check for magic character sequence indicating 'DONE'
+ */
+ if (strncmp(zLine, SH_DONE_MARK, SH_DONE_MARK_LEN) == 0)
+ break;
+
+ {
+ size_t llen = strlen(zLine);
+ if (text_sz <= used_ct + llen) {
+ realloc_text(&text, &text_sz, llen);
+ scan = text + used_ct;
+ }
+
+ memcpy(scan, zLine, llen);
+ used_ct += llen;
+ scan += llen;
+ }
+
+ /*
+ * Stop now if server timed out or if we are at EOF
+ */
+ if ((serv_id == NULLPROCESS) || feof(serv_pair.fp_read)) {
+ fputs(SHELL_NO_END_MARK_MSG, trace_fp);
+ break;
+ }
+ }
+
+ /*
+ * Trim off all trailing white space and shorten the buffer
+ * to the size actually used.
+ */
+ while ( (scan > text)
+ && IS_WHITESPACE_CHAR(scan[-1]))
+ scan--;
+ text_sz = (scan - text) + 1;
+ *scan = NUL;
+
+ if (OPT_VALUE_TRACE >= TRACE_SERVER_SHELL)
+ fprintf(trace_fp, TRACE_SHELL_RESULT_MSG,
+ (int)text_sz, text, zLine);
+
+ return AGREALOC((void*)text, text_sz, "resize output");
+#undef LOAD_RETRY_LIMIT
+}
+
+/**
+ * Run a semi-permanent server shell. The program will be the
+ * one named by the environment variable $SHELL, or default to "sh".
+ * If one of the commands we send to it takes too long or it dies,
+ * we will shoot it and restart one later.
+ *
+ * @param cmd the input command string
+ * @returns an allocated string, even if it is empty.
+ */
+LOCAL char *
+shell_cmd(char const * cmd)
+{
+ /*
+ * IF the shell server process is not running yet,
+ * THEN try to start it.
+ */
+ if (serv_id == NULLPROCESS) {
+ putenv((char *)SHELL_SET_PS4_FMT);
+ serv_id = server_fp_open(&serv_pair, server_args);
+ if (serv_id > 0)
+ server_setup();
+ }
+
+ /*
+ * IF it is still not running,
+ * THEN return the nil string.
+ */
+ if (serv_id <= 0) {
+ char* pz = (char*)AGALOC(1, "Text Block");
+
+ *pz = NUL;
+ return pz;
+ }
+
+ /*
+ * Make sure the process will pay attention to us,
+ * send the supplied command, and then
+ * have it output a special marker that we can find.
+ */
+ if (! send_cmd_ok(cmd))
+ return NULL;
+
+ /*
+ * Now try to read back all the data. If we fail due to either
+ * a sigpipe or sigalrm (timeout), we will return the nil string.
+ */
+ {
+ char* pz = load_data();
+ if (pz == NULL) {
+ fprintf(trace_fp, CMD_FAIL_FMT, cmd);
+ close_server_shell();
+ pz = (char*)AGALOC(1, "Text Block");
+
+ *pz = NUL;
+
+ } else if (was_close_err)
+ fprintf(trace_fp, CMD_FAIL_FMT, cmd);
+
+ last_cmd = NULL;
+ return pz;
+ }
+}
+
+#endif /* ! SHELL_ENABLED */
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/agShell.c */
diff --git a/agen5/agUtils.c b/agen5/agUtils.c
new file mode 100644
index 0000000..a774c09
--- /dev/null
+++ b/agen5/agUtils.c
@@ -0,0 +1,564 @@
+/**
+ * @file agUtils.c
+ *
+ * Various utilities for AutoGen.
+ *
+ * Time-stamp: "2012-03-10 13:11:54 bkorb"
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+define_base_name(void);
+
+static void
+put_defines_into_env(void);
+
+static char const *
+skip_quote(char const * qstr);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * Allocating printf function. It either works or kills the program.
+ * @param[in] pzFmt the input format
+ * @returns the allocated, formatted result string.
+ */
+LOCAL char *
+aprf(char const * pzFmt, ...)
+{
+ char* pz;
+ va_list ap;
+ va_start(ap, pzFmt);
+ (void)vasprintf(&pz, pzFmt, ap);
+ va_end(ap);
+
+ if (pz == NULL) {
+ char z[ 2 * SCRIBBLE_SIZE ];
+ snprintf(z, sizeof(z), APRF_ALLOCATE_FAIL, pzFmt);
+ AG_ABEND(z);
+ }
+ return pz;
+}
+
+/**
+ * Figure out what base name to use. --base-name was not specified.
+ * Base it on the definitions file, if available.
+ */
+static void
+define_base_name(void)
+{
+ char const * pz;
+ char* pzD;
+
+ if (! ENABLED_OPT(DEFINITIONS)) {
+ OPT_ARG(BASE_NAME) = DFT_BASE_NAME;
+ return;
+ }
+
+ pz = strrchr(OPT_ARG(DEFINITIONS), '/');
+ /*
+ * Point to the character after the last '/', or to the full
+ * definition file name, if there is no '/'.
+ */
+ pz = (pz == NULL) ? OPT_ARG(DEFINITIONS) : (pz + 1);
+
+ /*
+ * IF input is from stdin, then use "stdin"
+ */
+ if ((pz[0] == '-') && (pz[1] == NUL)) {
+ OPT_ARG(BASE_NAME) = STDIN_FILE_NAME;
+ return;
+ }
+
+ /*
+ * Otherwise, use the basename of the definitions file
+ */
+ OPT_ARG(BASE_NAME) = \
+ pzD = AGALOC(strlen(pz)+1, "derived base");
+
+ while ((*pz != NUL) && (*pz != '.')) *(pzD++) = *(pz++);
+ *pzD = NUL;
+}
+
+/**
+ * Put the -D option arguments into the environment.
+ * This makes them accessible to Guile/Scheme code, too.
+ */
+static void
+put_defines_into_env(void)
+{
+ int ct = STACKCT_OPT(DEFINE);
+ char const ** ppz = STACKLST_OPT(DEFINE);
+
+ do {
+ char const * pz = *(ppz++);
+ /*
+ * IF there is no associated value, THEN set it to '1'.
+ * There are weird problems with empty defines.
+ * FIXME: we loose track of this memory. Don't know what to do,
+ * really, there is no good recovery mechanism for environment
+ * data.
+ */
+ if (strchr(pz, '=') == NULL) {
+ size_t siz = strlen(pz)+3;
+ char* p = AGALOC(siz, "env define");
+
+ strcpy(p, pz);
+ strcpy(p+siz-3, DFT_ENV_VAL);
+ pz = p;
+ }
+
+ /*
+ * Now put it in the environment
+ */
+ putenv((char*)pz);
+ } while (--ct > 0);
+}
+
+/**
+ * Open trace output file.
+ *
+ * If the name starts with a pipe character (vertical bar), then
+ * use popen on the command. If it starts with ">>", then append
+ * to the file name that follows that.
+ *
+ * The trace output starts with the command and arguments used to
+ * start autogen.
+ *
+ * @param[in] av autogen's argument vector
+ * @param[in] odsc option descriptor with file name string argument
+ */
+LOCAL void
+open_trace_file(char ** av, tOptDesc * odsc)
+{
+ char const * fname = odsc->optArg.argString;
+
+ trace_is_to_pipe = (*fname == '|');
+ if (trace_is_to_pipe)
+ trace_fp = popen(++fname, "w");
+
+ else if ((fname[0] == '>') && (fname[1] == '>')) {
+ fname = SPN_WHITESPACE_CHARS(fname + 2);
+ trace_fp = fopen(fname, "a");
+ }
+
+ else
+ trace_fp = fopen(fname, "w");
+
+ if (trace_fp == NULL)
+ AG_ABEND(aprf(OPEN_ERROR_FMT, errno, strerror(errno), fname));
+
+#ifdef _IONBF
+ setvbuf(trace_fp, NULL, _IONBF, 0);
+#endif
+
+ fprintf(trace_fp, TRACE_START_FMT, (unsigned int)getpid(), *av);
+ while (*(++av) != NULL)
+ fprintf(trace_fp, TRACE_AG_ARG_FMT, *av);
+ putc(NL, trace_fp);
+}
+
+/**
+ * Check the environment for make dependency info. We look for
+ * AUTOGEN_MAKE_DEP, but if that is not found, we also look for
+ * DEPENDENCIES_OUTPUT. To do dependency tracking at all, we
+ * must find one of these environment variables and it must
+ *
+ * * be non-empty
+ * * not contain a variation on "no"
+ * * not contain a variation on "false"
+ *
+ * Furthermore, to specify a file name, the contents must not contain
+ * some variation on "yes" or "true".
+ */
+LOCAL void
+check_make_dep_env(void)
+{
+ bool have_opt_string = false;
+ bool set_opt = false;
+
+ char const * mdep = getenv(AG_MAKE_DEP_NAME);
+ if (mdep == NULL) {
+ mdep = getenv(DEP_OUT_NAME);
+ if (mdep == NULL)
+ return;
+ }
+ switch (*mdep) {
+ case NUL: break;
+ case '1':
+ set_opt = (mdep[0] == '1');
+ /* FALLTHROUGH */
+
+ case '0':
+ if (mdep[1] != NUL)
+ have_opt_string = true;
+ break;
+
+ case 'y':
+ case 'Y':
+ set_opt = true;
+ have_opt_string = (streqvcmp(mdep + 1, YES_NAME_STR+1) != 0);
+ break;
+
+ case 'n':
+ case 'N':
+ set_opt = \
+ have_opt_string = (streqvcmp(mdep + 1, NO_NAME_STR+1) != 0);
+ break;
+
+ case 't':
+ case 'T':
+ set_opt = true;
+ have_opt_string = (streqvcmp(mdep + 1, TRUE_NAME_STR+1) != 0);
+ break;
+
+ case 'f':
+ case 'F':
+ set_opt = \
+ have_opt_string = (streqvcmp(mdep + 1, FALSE_NAME_STR+1) != 0);
+ break;
+
+ default:
+ have_opt_string = \
+ set_opt = true;
+ }
+ if (! set_opt) return;
+ if (! have_opt_string) {
+ SET_OPT_MAKE_DEP("");
+ return;
+ }
+
+ {
+ char * pz = AGALOC(strlen(mdep) + 5, "mdep");
+ char * fp = pz;
+
+ *(pz++) = 'F';
+ for (;;) {
+ int ch = *(unsigned char *)(mdep++);
+ if (IS_END_TOKEN_CHAR(ch))
+ break;
+
+ *(pz++) = ch;
+ }
+ *pz = NUL;
+
+ SET_OPT_SAVE_OPTS(fp);
+ mdep = SPN_WHITESPACE_CHARS(mdep);
+ if (*mdep == NUL)
+ return;
+
+ pz = fp;
+ *(pz++) = 'T';
+ for (;;) {
+ int ch = *(unsigned char *)(mdep++);
+ if (IS_END_TOKEN_CHAR(ch))
+ break;
+
+ *(pz++) = ch;
+ }
+ *pz = NUL;
+ SET_OPT_SAVE_OPTS(fp);
+ AGFREE(fp);
+ }
+}
+
+LOCAL void
+process_ag_opts(int arg_ct, char ** arg_vec)
+{
+ /*
+ * Advance the argument counters and pointers past any
+ * command line options
+ */
+ {
+ int optCt = optionProcess(&autogenOptions, arg_ct, arg_vec);
+
+ /*
+ * Make sure we have a source file, even if it is "-" (stdin)
+ */
+ switch (arg_ct - optCt) {
+ case 1:
+ if (! HAVE_OPT(DEFINITIONS)) {
+ OPT_ARG(DEFINITIONS) = *(arg_vec + optCt);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ usage_message(DOOPT_TOO_MANY_DEFS, ag_pname);
+ /* NOTREACHED */
+
+ case 0:
+ if (! HAVE_OPT(DEFINITIONS))
+ OPT_ARG(DEFINITIONS) = DFT_DEF_INPUT_STR;
+ break;
+ }
+ }
+
+ if ((OPT_VALUE_TRACE > TRACE_NOTHING) && HAVE_OPT(TRACE_OUT))
+ open_trace_file(arg_vec, &DESC(TRACE_OUT));
+
+ start_time = time(NULL) - 1;
+
+ if (! HAVE_OPT(TIMEOUT))
+ OPT_ARG(TIMEOUT) = (char const *)AG_DEFAULT_TIMEOUT;
+
+ /*
+ * IF the definitions file has been disabled,
+ * THEN a template *must* have been specified.
+ */
+ if ( (! ENABLED_OPT(DEFINITIONS))
+ && (! HAVE_OPT(OVERRIDE_TPL)) )
+ AG_ABEND(NO_TEMPLATE_ERR_MSG);
+
+ /*
+ * IF we do not have a base-name option, then we compute some value
+ */
+ if (! HAVE_OPT(BASE_NAME))
+ define_base_name();
+
+ check_make_dep_env();
+
+ if (HAVE_OPT(MAKE_DEP))
+ start_dep_file();
+
+ strequate(OPT_ARG(EQUATE));
+
+ /*
+ * IF we have some defines to put in our environment, ...
+ */
+ if (HAVE_OPT(DEFINE))
+ put_defines_into_env();
+}
+
+/**
+ * look for a define string. It may be in our DEFINE option list
+ * (preferred result) or in the environment. Look up both.
+ *
+ * @param[in] de_name name to look for
+ * @param[in] check_env whether or not to look in environment
+ *
+ * @returns a pointer to the string, if found, or NULL.
+ */
+LOCAL char const *
+get_define_str(char const * de_name, bool check_env)
+{
+ char const ** ppz;
+ int ct;
+ if (HAVE_OPT(DEFINE)) {
+ ct = STACKCT_OPT( DEFINE);
+ ppz = STACKLST_OPT(DEFINE);
+
+ while (ct-- > 0) {
+ char const * pz = *(ppz++);
+ char * pzEq = strchr(pz, '=');
+ int res;
+
+ if (pzEq != NULL)
+ *pzEq = NUL;
+
+ res = strcmp(de_name, pz);
+ if (pzEq != NULL)
+ *pzEq = '=';
+
+ if (res == 0)
+ return (pzEq != NULL) ? pzEq+1 : zNil;
+ }
+ }
+ return check_env ? getenv(de_name) : NULL;
+}
+
+
+/**
+ * The following routine scans over quoted text, shifting it in the process
+ * and eliminating the starting quote, ending quote and any embedded
+ * backslashes. They may be used to embed the quote character in the quoted
+ * text. The quote character is whatever character the argument is pointing
+ * at when this procedure is called.
+ *
+ * @param[in,out] in_q input quoted string/output unquoted
+ * @returns the address of the byte after the original closing quote.
+ */
+LOCAL char *
+span_quote(char * in_q)
+{
+ char qc = *in_q; /* Save the quote character type */
+ char * dp = in_q++; /* Destination pointer */
+
+ while (*in_q != qc) {
+ switch (*dp++ = *in_q++) {
+ case NUL:
+ return in_q-1; /* Return address of terminating NUL */
+
+ case '\\':
+ if (qc != '\'') {
+ int ct = ao_string_cook_escape_char(in_q, dp-1, 0x7F);
+ if (dp[-1] == 0x7F) dp--;
+ in_q += ct;
+
+ } else {
+ switch (*in_q) {
+ case '\\':
+ case '\'':
+ case '#':
+ dp[-1] = *in_q++;
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ }
+
+ *dp = NUL;
+ return in_q+1; /* Return addr of char after the terminating quote */
+}
+
+/**
+ * The following routine skips over quoted text. The quote character is
+ * whatever character the argument is pointing at when this procedure is
+ * called.
+ *
+ * @param[in] qstr input quoted string/output unquoted
+ * @returns the address of the byte after the original closing quote.
+ */
+static char const *
+skip_quote(char const * qstr)
+{
+ char qc = *qstr++; /* Save the quote character type */
+
+ while (*qstr != qc) {
+ switch (*qstr++) {
+ case NUL:
+ return qstr-1; /* Return address of terminating NUL */
+
+ case '\\':
+ if (qc == '\'') {
+ /*
+ * Single quoted strings process the backquote specially
+ * only in fron of these three characters:
+ */
+ switch (*qstr) {
+ case '\\':
+ case '\'':
+ case '#':
+ qstr++;
+ }
+
+ } else {
+ char p[10]; /* provide a scratch pad for escape processing */
+ int ct = ao_string_cook_escape_char(qstr, p, 0x7F);
+ qstr += ct;
+ } /* if (q == '\'') */
+ } /* switch (*qstr++) */
+ } /* while (*qstr != q) */
+
+ return qstr+1; /* Return addr of char after the terminating quote */
+}
+
+/**
+ * Skip over scheme expression. We need to find what follows it.
+ * Guile will carefully parse it later.
+ *
+ * @param[in] scan where to start search
+ * @param[in] end point beyond which not to go
+ * @returns character after closing parenthesis or "end",
+ * which ever comes first.
+ */
+LOCAL char const *
+skip_scheme(char const * scan, char const * end)
+{
+ int level = 0;
+
+ for (;;) {
+ scan = BRK_SCHEME_NOTE_CHARS(scan);
+ if (scan >= end)
+ return end;
+
+ switch (*(scan++)) {
+ case '(':
+ level++;
+ break;
+
+ case ')':
+ if (--level == 0)
+ return scan;
+ break;
+
+ case '"':
+ scan = skip_quote(scan-1);
+ }
+ }
+}
+
+
+LOCAL int
+count_nl(char const * pz)
+{
+ int ct = 0;
+ for (;;) {
+ char const * p = strchr(pz, NL);
+ if (p == NULL)
+ break;
+ ct++;
+ pz = p + 1;
+ }
+ return ct;
+}
+
+
+LOCAL char const *
+skip_expr(char const * pzSrc, size_t len)
+{
+ char const * pzEnd = pzSrc + len;
+
+ guess_again:
+
+ pzSrc = SPN_WHITESPACE_CHARS(pzSrc);
+ if (pzSrc >= pzEnd)
+ return pzEnd;
+ switch (*pzSrc) {
+ case ';':
+ pzSrc = strchr(pzSrc, NL);
+ if (pzSrc == NULL)
+ return pzEnd;
+ goto guess_again;
+
+ case '(':
+ return skip_scheme(pzSrc, pzEnd);
+
+ case '"':
+ case '\'':
+ case '`':
+ pzSrc = skip_quote(pzSrc);
+ return (pzSrc > pzEnd) ? pzEnd : pzSrc;
+
+ default:
+ break;
+ }
+
+ pzSrc = BRK_WHITESPACE_CHARS(pzSrc);
+ return (pzSrc > pzEnd) ? pzEnd : pzSrc;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/agUtils.c */
diff --git a/agen5/autogen.1 b/agen5/autogen.1
new file mode 100644
index 0000000..ab9f82f
--- /dev/null
+++ b/agen5/autogen.1
@@ -0,0 +1,559 @@
+.TH autogen 1 "11 Aug 2012" "GNU AutoGen (5.16.2)" "User Commands"
+.\"
+.\" DO NOT EDIT THIS FILE (opts.man)
+.\"
+.\" It has been AutoGen-ed August 11, 2012 at 09:41:44 AM by AutoGen 5.16.2pre7
+.\" From the definitions /old-home/bkorb/ag/ag/agen5/opts.def
+.\" and the template file agman-cmd
+.\"
+.SH NAME
+autogen \- The Automated Program Generator
+.SH SYNOPSIS
+.B autogen
+.\" Mixture of short (flag) options and long options
+.RB [ \-\fIflag\fP " [\fIvalue\fP]]... [" \-\-\fIopt\-name\fP " [[=| ]\fIvalue\fP]]..." [ <def-file> ]
+.PP
+AutoGen creates text files from templates using external definitions.
+.SH DESCRIPTION
+\fBAutoGen\fP is designed for generating program files that contain
+repetitive text with varied substitutions. The goal is to simplify the
+maintenance of programs that contain large amounts of repetitious text.
+This is especially valuable if there are several blocks of such text
+that must be kept synchronized.
+.sp
+One common example is the problem of maintaining the code required for
+processing program options. Processing options requires a minimum of
+four different constructs be kept in proper order in different places
+in your program. You need at least: The flag character in the flag
+string, code to process the flag when it is encountered, a global
+state variable or two, and a line in the usage text.
+You will need more things besides this if you choose to implement
+long option names, configuration file processing, environment variables
+and so on.
+.sp
+All of this can be done mechanically; with the proper templates
+and this program.
+.SH "OPTIONS"
+.SS "The following options select definitions, templates and scheme functions to use"
+.TP
+.BR \-L " \fIdir\fP, " \-\-templ\-dirs "=" \fIdir\fP
+Template search directory list.
+This option may appear an unlimited number of times.
+.sp
+Add a directory to the list of directories to search when opening
+a template, either as the primary template or an included one.
+The last entry has the highest priority in the search list.
+That is to say, they are searched in reverse order.
+.TP
+.BR \-T " \fItpl\-file\fP, " \-\-override\-tpl "=" \fItpl\-file\fP
+Override template file.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+Definition files specify the standard template that is to be expanded.
+This option will override that name and expand a different template.
+.TP
+.BR \-l " \fItpl\-file\fP, " \-\-lib\-template "=" \fItpl\-file\fP
+Library template file.
+This option may appear an unlimited number of times.
+.sp
+DEFINE macros are saved from this template file for use in processing
+the main macro file. Template text aside from the DEFINE macros is
+is ignored.
+.TP
+.BR \-\-definitions "=\fIfile\fP", " \fB\-\-no\-definitions\fP"
+Definitions input file.
+The \fIno\-definitions\fP form will disable the option.
+This option is enabled by default.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+Use this argument to specify the input definitions file with a
+command line option. If you do not specify this option, then
+there must be a command line argument that specifies the file,
+even if only to specify stdin with a hyphen (\fB\-\fP).
+Specify, \fB\-\-no\-definitions\fP when you wish to process
+a template without any active AutoGen definitions.
+.TP
+.BR \-S " \fIfile\fP, " \-\-load\-scheme "=" \fIfile\fP
+Scheme code file to load.
+.sp
+Use this option to pre\-load Scheme scripts into the Guile
+interpreter before template processing begins.
+Please note that the AutoGen specific functions are not loaded
+until after argument processing. So, though they may be specified
+in lambda functions you define, they may not be invoked until after
+option processing is complete.
+.TP
+.BR \-F " \fIfile\fP, " \-\-load\-functions "=" \fIfile\fP
+Load scheme function library.
+.sp
+This option is used to load Guile\-scheme functions. The automatically
+called initialization routine \fBscm_init\fP must be used to register
+these routines or data.
+.TP
+.BR \-\-shell "=\fIshell\fP"
+name or path name of shell to use.
+.sp
+By default, when AutoGen is built, the configuration is probed for a
+reasonable Bourne\-like shell to use for shell script processing. If
+a particular template needs an alternate shell, it must be specified
+with this option on the command line, with an environment variable
+(\fBSHELL\fP) or in the configuration/initialization file.
+.TP
+.BR \-m ", " \-\-no\-fmemopen
+Do not use in\-mem streams.
+.sp
+If the local C library supports "\fBfopencookie(3GNU)\fP", or
+"\fBfunopen(3BSD)\fP" then AutoGen prefers to use in\-memory stream
+buffer opens instead of anonymous files. This may lead to problems
+if there is a shortage of virtual memory. If, for a particular
+application, you run out of memory, then specify this option.
+This is unlikely in a modern 64\-bit virtual memory environment.
+.sp
+On platforms without these functions, the option is accepted
+but ignored. \fBfmemopen(POSIX)\fP is not adequate because
+its string buffer is not reallocatable. \fBopen_memstream(POSIX)\fP
+is \fIalso\fP not adequate because the stream is only opened for
+output. AutoGen needs a reallocatable buffer available for both
+reading and writing.
+.TP
+.BR \-\-equate "=\fIchar\-list\fP"
+characters considered equivalent.
+The default \fIchar\-list\fP for this option is:
+.ti +4
+ _\-^
+.sp
+This option will alter the list of characters considered equivalent.
+The default are the three characters, "_\-^". (The last is conventional
+on a Tandem/HP\-NonStop, and I used to do a lot of work on Tandems.)
+.SS "The following options modify how output is handled"
+.TP
+.BR \-b " \fIname\fP, " \-\-base\-name "=" \fIname\fP
+Base name for output file(s).
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+A template may specify the exact name of the output file. Normally,
+it does not. Instead, the name is composed of the base name of the
+definitions file with suffixes appended. This option will override the
+base name derived from the definitions file name. This is required if
+there is no definitions file and advisable if definitions are being
+read from stdin. If the definitions are being read from standard in,
+the base name defaults to \fIstdin\fP. Any leading directory components
+in the name will be silently removed. If you wish the output file to
+appear in a particular directory, it is recommended that you "cd" into
+that directory first, or use directory names in the format specification
+for the output suffix lists, see: pseudo macro.
+.TP
+.BR \-\-source\-time, " \fB\-\-no\-source\-time\fP"
+set mod times to latest source.
+The \fIno\-source\-time\fP form will disable the option.
+.sp
+If you stamp your output files with the \fBDNE\fP macro output, then
+your output files will always be different, even if the content has
+not really changed. If you use this option, then the modification
+time of the output files will change only if the input files change.
+This will help reduce unneeded builds.
+.TP
+.BR \-\-writable, " \fB\-\-not\-writable\fP"
+Allow output files to be writable.
+The \fInot\-writable\fP form will disable the option.
+.sp
+This option will leave output files writable.
+Normally, output files are read\-only.
+.SS "The following options are often useful while debugging new templates"
+They specify limits that prevent the template from taking overly long
+or producing more output than expected.
+.sp
+.TP
+.BR \-\-loop\-limit "=\fIlim\fP"
+Limit on increment loops.
+This option takes an integer number as its argument.
+The value of \fIlim\fP is constrained to being:
+.in +4
+.nf
+.na
+exactly \-1, or
+in the range 1 through 0x1000000
+.fi
+.in -4
+The default \fIlim\fP for this option is:
+.ti +4
+ 256
+.sp
+This option prevents runaway loops. For example, if you accidentally
+specify, "FOR x (for\-from 1) (for\-to \-1) (for\-by 1)", it will take a
+long time to finish. If you do have more than 256 entries in tables,
+you will need to specify a new limit with this option.
+.TP
+.BR \-t " \fItime\-lim\fP, " \-\-timeout "=" \fItime\-lim\fP
+Time limit for server shell.
+This option takes an integer number as its argument.
+The value of \fItime\-lim\fP is constrained to being:
+.in +4
+.nf
+.na
+in the range 0 through 3600
+.fi
+.in -4
+.sp
+AutoGen works with a shell server process. Most normal commands will
+complete in less than 10 seconds. If, however, your commands need more
+time than this, use this option.
+.sp
+The valid range is 0 to 3600 seconds (1 hour).
+Zero will disable the server time limit.
+.TP
+.BR \-\-trace "=\fIlevel\fP"
+tracing level of detail.
+This option takes a keyword as its argument. The argument sets an enumeration value that can
+be tested by comparing them against the option value macro.
+The available keywords are:
+.in +4
+.nf
+.na
+nothing debug\-message server\-shell
+templates block\-macros expressions
+everything
+.fi
+or their numeric equivalent.
+.in -4
+.sp
+The default \fIlevel\fP for this option is:
+.ti +4
+ nothing
+.sp
+This option will cause AutoGen to display a trace of its template
+processing. There are six levels, each level including messages from
+the previous levels:
+.sp
+.sp
+.IR "nothing"
+Does no tracing at all (default)
+.sp
+.sp
+.IR "debug\-message"
+Print messages from the "DEBUG" AutoGen macro (see: DEBUG).
+.sp
+.sp
+.IR "server\-shell"
+Traces all input and output to the server shell. This includes a shell
+"independent" initialization script about 30 lines long. Its output is
+discarded and not inserted into any template.
+.sp
+.sp
+.IR "templates"
+Traces the invocation of \fBDEFINE\fPd macros and \fBINCLUDE\fPs
+.sp
+.sp
+.IR "block\-macros"
+Traces all block macros. The above, plus \fBIF\fP, \fBFOR\fP,
+\fBCASE\fP and \fBWHILE\fP.
+.sp
+.sp
+.IR "expressions"
+Displays the results of expression evaluations.
+.sp
+.sp
+.IR "everything"
+Displays the invocation of every AutoGen macro, even \fBTEXT\fP macros
+(i.e. the text outside of macro quotes). Additionally, if you rebuild
+the ``expr.ini'' file with debugging enabled, then all calls to
+AutoGen defined scheme functions will also get logged:
+.br
+.nf
+ cd ${top_builddir}/agen5
+ DEBUG_ENABLED=true bash bootstrap.dir expr.ini
+ make CFLAGS='\-g \-DDEBUG_ENABLED=1'
+.fi
+.sp
+Be aware that you cannot rebuild this source in this way without first
+having installed the \fBautogen\fP executable in your search path.
+Because of this, "expr.ini" is in the distributed source list, and
+not in the dependencies.
+.br
+.TP
+.BR \-\-trace\-out "=\fIfile\fP"
+tracing output file or filter.
+.sp
+The output specified may be a file name, a file that is appended to,
+or, if the option argument begins with the \fBpipe\fP operator
+(\fB|\fP), a command that will receive the tracing output as standard
+in. For example, \fB\-\-traceout='| less'\fP will run the trace output
+through the \fBless\fP program. Appending to a file is specified by
+preceeding the file name with two greater\-than characters (\fB>>\fP).
+.TP
+.BR \-\-show\-defs
+Show the definition tree.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+This will print out the complete definition tree before processing
+the template.
+.TP
+.BR \-\-used\-defines
+Show the definitions used.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+This will print out the names of definition values searched for
+during the processing of the template, whether actually found or
+not. There may be other referenced definitions in a template in
+portions of the template not evaluated. Some of the names listed
+may be computed names and others AutoGen macro arguments. This is
+not a means for producing a definitive, all\-encompassing list of all
+and only the values used from a definition file. This is intended
+as an aid to template documentation only.
+.TP
+.BR \-C ", " \-\-core
+Leave a core dump on a failure exit.
+.sp
+Many systems default to a zero sized core limit. If the system
+has the sys/resource.h header and if this option is supplied,
+then in the failure exit path, autogen will attempt to set the
+soft core limit to whatever the hard core limit is. If that
+does not work, then an administrator must raise the hard core
+size limit.
+.SS "These options can be used to control what gets processed
+in the definitions files and template files"
+They specify which outputs and parts of outputs to produce.
+.sp
+.TP
+.BR \-s " \fIsuffix\fP, " \-\-skip\-suffix "=" \fIsuffix\fP
+Omit the file with this suffix.
+This option may appear an unlimited number of times.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+This option must not appear in combination with any of the following options:
+select\-suffix.
+.sp
+Occasionally, it may not be desirable to produce all of the output
+files specified in the template. (For example, only the \fI.h\fP
+header file, but not the \fI.c\fP program text.) To do this
+specify \fB\-\-skip\-suffix=c\fP on the command line.
+.TP
+.BR \-o " \fIsuffix\fP, " \-\-select\-suffix "=" \fIsuffix\fP
+specify this output suffix.
+This option may appear an unlimited number of times.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+If you wish to override the suffix specifications in the template,
+you can use one or more copies of this option. See the suffix
+specification in the @ref{pseudo macro} section of the info doc.
+.TP
+.BR \-D " \fIvalue\fP, " \-\-define "=" \fIvalue\fP
+name to add to definition list.
+This option may appear an unlimited number of times.
+.sp
+The AutoGen define names are used for the following purposes:
+.sp
+.sp 1
+Sections of the AutoGen definitions may be enabled or disabled
+by using C\-style #ifdef and #ifndef directives.
+.sp 1
+When defining a value for a name, you may specify the index
+for a particular value. That index may be a literal value,
+a define option or a value #define\-d in the definitions themselves.
+.sp 1
+The name of a file may be prefixed with \fB$NAME/\fP.
+The \fB$NAME\fP part of the name string will be replaced with
+the define\-d value for \fBNAME\fP.
+.sp 1
+When AutoGen is finished loading the definitions, the defined values
+are exported to the environment with, \fBputenv(3)\fP.
+These values can then be used in shell scripts with \fB${NAME@\fP}
+references and in templates with \fB(getenv "NAME")\fP.
+.sp 1
+While processing a template, you may specify an index to retrieve
+a specific value. That index may also be a define\-d value.
+.br
+.sp
+It is entirely equivalent to place this name in the exported environment.
+Internally, that is what AutoGen actually does with this option.
+.TP
+.BR \-U " \fIname\-pat\fP, " \-\-undefine "=" \fIname\-pat\fP
+definition list removal pattern.
+This option may appear an unlimited number of times.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+Similar to 'C', AutoGen uses \fB#ifdef/#ifndef\fP preprocessing
+directives. This option will cause the matching names to be
+removed from the list of defined values.
+.SS "This option is used to automate dependency tracking"
+.TP
+.BR \-M " \fItype\fP, " \-\-make\-dep [ =\fItype\fP ]
+emit make dependency file.
+This option may appear an unlimited number of times.
+This option may not be preset with environment variables
+or in initialization (rc) files.
+.sp
+.sp
+This option behaves fairly closely to the way the \fB\-M\fP series of
+options work with the gcc compiler, except that instead of just
+emitting the predecessor dependencies, this also emits the successor
+dependencies (output target files). By default, the output dependency
+information will be placed in \fB<base\-name>.d\fP, but may also be
+specified with \fB\-MF<file>\fP. The time stamp on this file will be
+manipulated so that it will be one second older than the oldest
+primary output file.
+.sp
+The target in this dependency file will normally be the dependency
+file name, but may also be overridden with \fB\-MT<targ\-name>\fP.
+AutoGen will not alter the contents of that file, but it may create
+it and it will adjust the modification time to match the start time.
+.sp
+\fBNB:\fP these second letters are part of the option argument, so
+\fB\-MF <file>\fP must have the space character quoted or omitted, and
+\fB\-M "F <file>"\fP is acceptable because the \fBF\fP is part of the
+option argument.
+.sp
+\fB\-M\fP may be followed by any of the letters M, F, P, T, Q, D, or G.
+However, only F, Q, T and P are meaningful. All but F have somewhat
+different meanings. \fB\-MT<name>\fP is interpreted as meaning
+\fB<name>\fP is a sentinel file that will depend on all inputs
+(templates and definition files) and all the output files will depend
+on this sentinel file. It is suitable for use as a real make target.
+Q is treated identically to T, except dollar characters ('$') are
+doubled. P causes a special clean (clobber) phoney rule to be inserted
+into the make file fragment. An empty rule is always created for
+building the list of targets.
+.sp
+This is the recommended usage:
+.nf
+ \-MFwhatever\-you\-like.dep \-MTyour\-sentinel\-file \-MP
+.fi
+and then in your \fBMakefile\fP, make the \fIautogen\fP rule:
+.nf
+ \-include whatever\-you\-like.dep
+ clean_targets += clean\-your\-sentinel\-file
+ your\-sentinel\-file:
+ autogen \-MT$@ \-MF$*.d .....
+ local\-clean :
+ rm \-f $(clean_targets)
+.fi
+.sp
+The modification time on the dependency file is adjusted to be one
+second before the earliest time stamp of any other output file.
+Consequently, it is suitable for use as the sentinel file testifying
+to the fact the program was successfully run. (\fB\-include\fP is
+the GNU make way of specifying "include it if it exists". Your make
+must support that feature or your bootstrap process must create the
+file.)
+.sp
+All of this may also be specified using the \fBDEPENDENCIES_OUTPUT\fP
+or \fBAUTOGEN_MAKE_DEP\fP environment variables. If defined,
+dependency information will be output. If defined with white space
+free text that is something other than \fBtrue\fP, \fBfalse\fP,
+\fByes\fP, \fBno\fP, \fB0\fP or \fB1\fP, then the string is taken
+to be an output file name. If it contains a string of white space
+characters, the first token is as above and the second token is taken
+to be the target (sentinel) file as \fB\-MT\fP in the paragraphs
+above. \fBDEPENDENCIES_OUTPUT\fP will be ignored if there are
+multiple sequences of white space characters or if its contents are,
+specifically, \fBfalse\fP, \fBno\fP or \fB0\fP.
+.TP
+.BR \-? , " \-\-help"
+Display usage information and exit.
+.TP
+.BR \-! , " \-\-more\-help"
+Pass the extended usage information through a pager.
+.TP
+.BR \-> " [\fIrcfile\fP]," " \-\-save\-opts" "[=\fIrcfile\fP]"
+Save the option state to \fIrcfile\fP. The default is the \fIlast\fP
+configuration file listed in the \fBOPTION PRESETS\fP section, below.
+.TP
+.BR \-< " \fIrcfile\fP," " \-\-load\-opts" "=\fIrcfile\fP," " \-\-no\-load\-opts"
+Load options from \fIrcfile\fP.
+The \fIno\-load\-opts\fP form will disable the loading
+of earlier RC/INI files. \fI\-\-no\-load\-opts\fP is handled early,
+out of order.
+.TP
+.BR \-v " [{\fIv|c|n\fP}]," " \-\-version" "[=\fI{v|c|n}\fP]"
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.sp
+.SH "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from configuration ("RC" or ".INI") file(s) and values from
+environment variables named:
+.nf
+ \fBAUTOGEN_<option-name>\fP or \fBAUTOGEN\fP
+.fi
+.ad
+The environmental presets take precedence (are processed later than)
+the configuration files.
+The \fIhomerc\fP files are "\fI$HOME\fP", and "\fI.\fP".
+If any of these are directories, then the file \fI.autogenrc\fP
+is searched for within those directories.
+.SH "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.SH "FILES"
+See \fBOPTION PRESETS\fP for configuration files.
+.SH EXAMPLES
+Here is how the man page is produced:
+.br
+.in +4
+.nf
+autogen \-Tagman\-cmd.tpl \-MFman\-dep \-MTstamp\-man opts.def
+.in -4
+.fi
+.sp
+This command produced this man page from the AutoGen option definition
+file. It overrides the template specified in \fIopts.def\fP (normally
+\fIoptions.tpl\fP) and uses \fIagman\-cmd.tpl\fP. It also sets the
+make file dependency output to \fIman\-dep\fP and the sentinel file
+(time stamp file) to \fIman\-stamp\fP. The base of the file name is
+derived from the defined \fBprog\-name\fP.
+.sp
+The texi invocation document is produced via:
+.br
+.in +4
+.nf
+autogen \-Tagtexi\-cmd.tpl \-MFtexi\-dep \-MTtexi\-stamp opts.def
+.in -4
+.fi
+.SH "EXIT STATUS"
+One of the following exit values will be returned:
+.TP
+.BR 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.TP
+.BR 1 " (EXIT_OPTION_ERROR)"
+The command options were misconfigured.
+.TP
+.BR 2 " (EXIT_BAD_TEMPLATE)"
+An error was encountered processing the template.
+.TP
+.BR 3 " (EXIT_BAD_DEFINITIONS)"
+The definitions could not be deciphered.
+.TP
+.BR 4 " (EXIT_LOAD_ERROR)"
+An error was encountered during the load phase.
+.TP
+.BR 5 " (EXIT_SIGNAL)"
+Program exited due to catching a signal. If your template includes
+string formatting, a number argument to a "%s" formatting element will
+trigger a segmentation fault. Autogen will catch the seg fault signal
+and exit with \fBAUTOGEN_EXIT_SIGNAL(5)\fP. Alternatively, AutoGen
+may have been interrupted with a \fBkill(2)\fP signal.
+.TP
+.BR 66 " (EX_NOINPUT)"
+A specified configuration file could not be loaded.
+.TP
+.BR 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen-users@lists.sourceforge.net. Thank you.
+.SH "AUTHORS"
+Bruce Korb
+.SH "COPYRIGHT"
+Copyright (C) 1992-2012 Bruce Korb all rights reserved.
+This program is released under the terms of the GNU General Public License, version 3 or later.
+.SH "BUGS"
+Please send bug reports to: autogen-users@lists.sourceforge.net
+.SH "NOTES"
+This manual page was \fIAutoGen\fP-erated from the \fBautogen\fP
+option definitions.
diff --git a/agen5/autogen.c b/agen5/autogen.c
new file mode 100644
index 0000000..73ea05c
--- /dev/null
+++ b/agen5/autogen.c
@@ -0,0 +1,657 @@
+
+/**
+ * @file autogen.c
+ *
+ * Time-stamp: "2012-06-10 12:17:05 bkorb"
+ *
+ * This is the main routine for autogen.
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+typedef void (void_main_proc_t)(int, char **);
+
+typedef enum {
+ EXIT_PCLOSE_WAIT,
+ EXIT_PCLOSE_NOWAIT
+} wait_for_pclose_enum_t;
+
+#define _State_(n) #n,
+static char const * const state_names[] = { STATE_TABLE };
+#undef _State_
+
+static sigjmp_buf abendJumpEnv;
+static int abendJumpSignal = 0;
+
+typedef void (sighandler_proc_t)(int sig);
+static sighandler_proc_t ignore_signal, catch_sig_and_bail;
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+inner_main(void * closure, int argc, char ** argv);
+
+static void
+exit_cleanup(wait_for_pclose_enum_t cl_wait);
+
+static void
+cleanup_and_abort(int sig);
+
+static void
+catch_sig_and_bail(int sig);
+
+static void
+ignore_signal(int sig);
+
+static void
+done_check(void);
+
+static void
+setup_signals(sighandler_proc_t * chldHandler,
+ sighandler_proc_t * abrtHandler,
+ sighandler_proc_t * dfltHandler);
+/* = = = END-STATIC-FORWARD = = = */
+
+#ifndef HAVE_CHMOD
+# include "compat/chmod.c"
+#endif
+
+/**
+ * main routine under Guile guidance
+ *
+ * @param closure Guile closure parameter. Not used.
+ * @param argc argument count
+ * @param argv argument vector
+ */
+static void
+inner_main(void * closure, int argc, char ** argv)
+{
+ atexit(done_check);
+ initialize(argc, argv);
+
+ processing_state = PROC_STATE_LOAD_DEFS;
+ read_defs();
+
+ /*
+ * Activate the AutoGen specific Scheme functions.
+ * Then load, process and unload the main template
+ */
+ processing_state = PROC_STATE_LOAD_TPL;
+
+ {
+ templ_t* pTF = tpl_load(tpl_fname, NULL);
+
+ processing_state = PROC_STATE_EMITTING;
+ process_tpl(pTF);
+
+ processing_state = PROC_STATE_CLEANUP;
+ cleanup(pTF);
+ }
+
+ processing_state = PROC_STATE_DONE;
+ setup_signals(SIG_DFL, SIG_IGN, SIG_DFL);
+ exit_code = AUTOGEN_EXIT_SUCCESS;
+ done_check();
+ /* NOTREACHED */
+ (void)closure;
+}
+
+/**
+ * main() called from _start()
+ *
+ * @param argc argument count
+ * @param argv argument vector
+ * @return nothing -- Guile never returns, but calls exit(2).
+ */
+int
+main(int argc, char ** argv)
+{
+ /*
+ * IF we've been kicked with a signal,
+ * THEN abort, passing the signal that whacked us.
+ */
+ if (sigsetjmp(abendJumpEnv, 0) != 0)
+ cleanup_and_abort(abendJumpSignal);
+
+ setup_signals(ignore_signal, SIG_DFL, catch_sig_and_bail);
+ optionSaveState(&autogenOptions);
+ trace_fp = stderr;
+
+ /*
+ * as of 2.0.2, Guile will fiddle with strings all on its own accord.
+ * Coerce the environment into being POSIX ASCII strings so it keeps
+ * its bloody stinking nose out of our data.
+ */
+ putenv((char*)(void*)LC_ALL_IS_C);
+
+ /*
+ * If GUILE_WARN_DEPRECATED has not been defined, then likely we are
+ * not in a development environment and likely we don't want to give
+ * our users any angst.
+ */
+ if (getenv(GUILE_WARN_DEP_STR) == NULL)
+ putenv((char*)(void*)GUILE_WARN_NO_ENV);
+
+ AG_SCM_BOOT_GUILE(argc, argv, inner_main);
+
+ /* NOT REACHED */
+ return EXIT_FAILURE;
+}
+
+/**
+ * This code must run regardless of which exit path is taken
+ *
+ * @param [in] cl_wait Whether or not a child process should be waited for.
+ */
+static void
+exit_cleanup(wait_for_pclose_enum_t cl_wait)
+{
+ /*
+ * There are contexts wherein this function can get run twice.
+ */
+ {
+ static int exit_cleanup_done = 0;
+ if (exit_cleanup_done) {
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, EXIT_CLEANUP_MULLIGAN);
+ return;
+ }
+
+ exit_cleanup_done = 1;
+ }
+
+#ifdef SHELL_ENABLED
+ SCM_EVAL_CONST(EXIT_CLEANUP_STR);
+#endif
+
+ close_server_shell();
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, EXIT_CLEANUP_DONE_FMT,
+ (cl_wait == EXIT_PCLOSE_WAIT)
+ ? EXIT_CLEANUP_WAITED : EXIT_CLEANUP_NOWAIT,
+ (unsigned int)getpid());
+
+ do {
+ if (trace_fp == stderr)
+ break;
+
+ if (! trace_is_to_pipe) {
+ fclose(trace_fp);
+ break;
+ }
+
+
+ fflush(trace_fp);
+ pclose(trace_fp);
+ if (cl_wait == EXIT_PCLOSE_WAIT) {
+ int status;
+ while (wait(&status) > 0) ;
+ }
+ } while (false);
+
+ fflush(stdout);
+ fflush(stderr);
+}
+
+/**
+ * A signal was caught, siglongjmp called and main() has called this.
+ * We do not deallocate stuff so it can be found in the core dump.
+ *
+ * @param[in] sig the signal number
+ */
+static void
+cleanup_and_abort(int sig)
+{
+ if (*oops_pfx != NUL) {
+ fputs(oops_pfx, stderr);
+ oops_pfx = zNil;
+ }
+
+ fprintf(stderr, AG_SIG_ABORT_FMT, sig, strsignal(sig),
+ ((unsigned)processing_state <= PROC_STATE_DONE)
+ ? state_names[ processing_state ] : BOGUS_TAG);
+
+ if (processing_state == PROC_STATE_ABORTING) {
+ exit_cleanup(EXIT_PCLOSE_NOWAIT);
+ _exit(sig + 128);
+ }
+
+ processing_state = PROC_STATE_ABORTING;
+ setup_signals(SIG_DFL, SIG_DFL, SIG_DFL);
+
+ /*
+ * IF there is a current template, then we should report where we are
+ * so that the template writer knows where to look for their problem.
+ */
+ if (current_tpl != NULL) {
+ int line;
+ mac_func_t fnCd;
+ char const * pzFn;
+ char const * pzFl;
+
+ if (cur_macro == NULL) {
+ pzFn = PSEUDO_MACRO_NAME_STR;
+ line = 0;
+ fnCd = (mac_func_t)-1;
+
+ } else {
+ mac_func_t f =
+ (cur_macro->md_code > FUNC_CT)
+ ? FTYP_SELECT : cur_macro->md_code;
+ pzFn = ag_fun_names[ f ];
+ line = cur_macro->md_line;
+ fnCd = cur_macro->md_code;
+ }
+ pzFl = current_tpl->td_file;
+ if (pzFl == NULL)
+ pzFl = NULL_FILE_NAME_STR;
+ fprintf(stderr, AG_ABORT_LOC_FMT, pzFl, line, pzFn, fnCd);
+ }
+
+#ifdef HAVE_SYS_RESOURCE_H
+ /*
+ * If `--core' has been specified and if we have get/set rlimit,
+ * then try to set the core limit to the "hard" maximum before aborting.
+ */
+ if (HAVE_OPT(CORE)) {
+ struct rlimit rlim;
+ getrlimit(RLIMIT_CORE, &rlim);
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit(RLIMIT_CORE, &rlim);
+ }
+#endif
+
+ exit_cleanup(EXIT_PCLOSE_NOWAIT);
+ abort();
+}
+
+
+/**
+ * catch_sig_and_bail catches signals we abend on. The "siglongjmp"
+ * goes back to the real "main()" procedure and it will call
+ * "cleanup_and_abort()", above.
+ *
+ * @param[in] sig the signal number
+ */
+static void
+catch_sig_and_bail(int sig)
+{
+ switch (processing_state) {
+ case PROC_STATE_ABORTING:
+ exit_code = AUTOGEN_EXIT_SIGNAL;
+
+ case PROC_STATE_DONE:
+ break;
+
+ default:
+ abendJumpSignal = sig;
+ exit_code = AUTOGEN_EXIT_SIGNAL;
+ siglongjmp(abendJumpEnv, sig);
+ }
+}
+
+
+/**
+ * ignore_signal is the handler for SIGCHLD. If we set it to default,
+ * it will kill us. If we set it to ignore, it will be inherited.
+ * Therefore, always in all programs set it to call a procedure.
+ * The "wait(3)" call will do magical things, but will not override SIGIGN.
+ *
+ * @param[in] sig the signal number
+ */
+static void
+ignore_signal(int sig)
+{
+ (void)sig;
+ return;
+}
+
+
+/**
+ * This is _always_ called for exit processing.
+ * This only returns if "exit(3C)" is called during option processing.
+ * We have no way of determining the correct exit code.
+ * (Requested version or help exits EXIT_SUCCESS. Option failures
+ * are EXIT_FAILURE. We cannot determine here.)
+ */
+static void
+done_check(void)
+{
+ /*
+ * There are contexts wherein this function can get called twice.
+ */
+ {
+ static int done_check_done = 0;
+ if (done_check_done) {
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, DONE_CHECK_REDONE);
+ return;
+ }
+ done_check_done = 1;
+ }
+
+ switch (processing_state) {
+ case PROC_STATE_EMITTING:
+ case PROC_STATE_INCLUDING:
+ /*
+ * A library (viz., Guile) procedure has called exit(3C). The
+ * AutoGen abort paths all set processing_state to PROC_STATE_ABORTING.
+ */
+ if (*oops_pfx != NUL) {
+ /*
+ * Emit the CGI page header for an error message. We will rewind
+ * stderr and write the contents to stdout momentarily.
+ */
+ fputs(oops_pfx, stdout);
+ oops_pfx = zNil;
+ }
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ scm_backtrace();
+
+ fprintf(stderr, SCHEME_EVAL_ERR_FMT, current_tpl->td_file,
+ cur_macro->md_line);
+
+ /*
+ * We got here because someone called exit early.
+ */
+ do {
+#ifndef DEBUG_ENABLED
+ out_close(false);
+#else
+ out_close(true);
+#endif
+ } while (cur_fpstack->stk_prev != NULL);
+ exit_code = AUTOGEN_EXIT_BAD_TEMPLATE;
+ break; /* continue failure exit */
+
+ case PROC_STATE_LOAD_DEFS:
+ exit_code = AUTOGEN_EXIT_BAD_DEFINITIONS;
+ /* FALLTHROUGH */
+
+ default:
+ fprintf(stderr, AG_ABEND_STATE_FMT, state_names[processing_state]);
+ goto autogen_aborts;
+
+ case PROC_STATE_ABORTING:
+ exit_code = AUTOGEN_EXIT_BAD_TEMPLATE;
+
+ autogen_aborts:
+ if (*oops_pfx != NUL) {
+ /*
+ * Emit the CGI page header for an error message. We will rewind
+ * stderr and write the contents to stdout momentarily.
+ */
+ fputs(oops_pfx, stdout);
+ oops_pfx = zNil;
+ }
+ break; /* continue failure exit */
+
+ case PROC_STATE_OPTIONS:
+ /* Exiting in option processing state is verbose enough */
+ break;
+
+ case PROC_STATE_DONE:
+ break; /* continue normal exit */
+ }
+
+ if (last_scm_cmd != NULL)
+ fprintf(stderr, GUILE_CMD_FAIL_FMT, last_scm_cmd);
+
+ /*
+ * IF we diverted stderr, then now is the time to copy the text to stdout.
+ * This is done for CGI mode wherein we produce an error page in case of
+ * an error, but otherwise discard stderr.
+ */
+ if (cgi_stderr != NULL) {
+ do {
+ long pos = ftell(stderr);
+ char* pz;
+
+ /*
+ * Don't bother with the overhead if there is no work to do.
+ */
+ if (pos <= 0)
+ break;
+ pz = AGALOC(pos, "stderr redir");
+ rewind(stderr);
+ fread( pz, (size_t)1, (size_t)pos, stderr);
+ fwrite(pz, (size_t)1, (size_t)pos, stdout);
+ AGFREE(pz);
+ } while (false);
+
+ unlink(cgi_stderr);
+ AGFREE(cgi_stderr);
+ cgi_stderr = NULL;
+ }
+
+ ag_scribble_deinit();
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, DONE_CHECK_DONE);
+
+ exit_cleanup(EXIT_PCLOSE_WAIT);
+
+ /*
+ * When processing options, return so that the option processing exit code
+ * is used. Otherwise, terminate execution now with the decided upon
+ * exit code. (Always EXIT_FAILURE, unless this was called at the end
+ * of inner_main().)
+ */
+ if (processing_state != PROC_STATE_OPTIONS)
+ _exit(exit_code);
+}
+
+
+LOCAL void
+ag_abend_at(char const * pzMsg
+#ifdef DEBUG_ENABLED
+ , char const * pzFile, int line
+#endif
+ )
+{
+ if (*oops_pfx != NUL) {
+ fputs(oops_pfx, stderr);
+ oops_pfx = zNil;
+ }
+
+#ifdef DEBUG_ENABLED
+ fprintf(stderr, "Giving up in %s line %d\n", pzFile, line);
+#endif
+
+ if ((processing_state >= PROC_STATE_LIB_LOAD) && (current_tpl != NULL)) {
+ int l_no = (cur_macro == NULL) ? -1 : cur_macro->md_line;
+ fprintf(stderr, ERROR_IN_TPL_FMT, current_tpl->td_file, l_no);
+ }
+ fputs(pzMsg, stderr);
+ pzMsg += strlen(pzMsg);
+ if (pzMsg[-1] != NL)
+ fputc(NL, stderr);
+
+ {
+ proc_state_t oldState = processing_state;
+ processing_state = PROC_STATE_ABORTING;
+
+ switch (oldState) {
+ case PROC_STATE_EMITTING:
+ case PROC_STATE_INCLUDING:
+ case PROC_STATE_CLEANUP:
+ longjmp(abort_jmp_buf, FAILURE);
+ /* NOTREACHED */
+ default:
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+ }
+}
+
+
+static void
+setup_signals(sighandler_proc_t * chldHandler,
+ sighandler_proc_t * abrtHandler,
+ sighandler_proc_t * dfltHandler)
+{
+ struct sigaction sa;
+ int sigNo = 1;
+#ifdef SIGRTMIN
+ const int maxSig = SIGRTMIN-1;
+#else
+ const int maxSig = NSIG;
+#endif
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
+ do {
+ switch (sigNo) {
+ /*
+ * Signal handling for SIGCHLD needs to be ignored. Do *NOT* use
+ * SIG_IGN to do this. That gets inherited by programs that need
+ * to be able to use wait(2) calls. At the same time, we don't
+ * want our children to become zombies. We may run out of zombie
+ * space. Therefore, set the handler to an empty procedure.
+ * POSIX oversight. Allowed to be fixed for next POSIX rev, tho
+ * it is "optional" to reset SIGCHLD on exec(2).
+ */
+#ifndef SIGCHLD
+# define SIGCHLD SIGCLD
+#endif
+ case SIGCHLD:
+ sa.sa_handler = chldHandler;
+ break;
+
+ case SIGABRT:
+ sa.sa_handler = abrtHandler;
+ break;
+
+ /*
+ * Signals we must leave alone.
+ */
+ case SIGKILL:
+ case SIGSTOP:
+
+ /*
+ * Signals we choose to leave alone.
+ */
+#ifdef SIGTSTP
+ case SIGTSTP:
+#endif
+ continue;
+
+#if defined(DEBUG_ENABLED)
+ case SIGBUS:
+ case SIGSEGV:
+ /*
+ * While debugging AutoGen, we want seg faults to happen and
+ * trigger core dumps. Make sure this happens.
+ */
+ sa.sa_handler = SIG_DFL;
+ break;
+#endif
+
+ /*
+ * Signals to ignore with SIG_IGN.
+ */
+ case 0: /* cannot happen, but the following might not be defined */
+#ifdef SIGWINCH
+ case SIGWINCH:
+#endif
+#ifdef SIGTTIN
+ case SIGTTIN: /* tty input */
+#endif
+#ifdef SIGTTOU
+ case SIGTTOU: /* tty output */
+#endif
+ sa.sa_handler = SIG_IGN;
+ break;
+
+#ifdef DAEMON_ENABLED
+# error DAEMON-ization of AutoGen is not ready for prime time
+ Choke Me.
+ case SIGHUP:
+ if (HAVE_OPT(DAEMON)) {
+ sa.sa_handler = handleSighup;
+ break;
+ }
+ /* FALLTHROUGH */
+#endif
+
+ default:
+ sa.sa_handler = dfltHandler;
+ }
+ sigaction(sigNo, &sa, NULL);
+ } while (++sigNo < maxSig);
+}
+
+#ifndef HAVE_STRFTIME
+# include <compat/strftime.c>
+#endif
+
+#ifndef HAVE_STRSIGNAL
+# include <compat/strsignal.c>
+#endif
+
+LOCAL void *
+ao_malloc (size_t sz)
+{
+ void * res = malloc(sz);
+ if (res == NULL) {
+ fprintf(stderr, MALLOC_FAIL_FMT, sz);
+ exit(EXIT_FAILURE);
+ }
+ return res;
+}
+
+
+LOCAL void *
+ao_realloc (void *p, size_t sz)
+{
+ void * res = (p == NULL) ? malloc(sz) : realloc(p, sz);
+ if (res == NULL) {
+ fprintf(stderr, REALLOC_FAIL_FMT, sz, p);
+ exit(EXIT_FAILURE);
+ }
+ return res;
+}
+
+LOCAL char *
+ao_strdup (char const * str)
+{
+ char * res = strdup(str);
+ if (res == NULL) {
+ fprintf(stderr, STRDUP_FAIL_FMT, (int)strlen(str));
+ exit(EXIT_FAILURE);
+ }
+ return res;
+}
+
+#ifdef __GNUC__
+ void ignore_vars(void);
+ void ignore_vars(void) {
+ (void)option_load_mode, (void)program_pkgdatadir;
+ }
+#endif
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/autogen.c */
diff --git a/agen5/autogen.h b/agen5/autogen.h
new file mode 100644
index 0000000..6e4551a
--- /dev/null
+++ b/agen5/autogen.h
@@ -0,0 +1,570 @@
+
+/**
+ * @file autogen.h
+ *
+ * Time-stamp: "2012-04-07 09:45:02 bkorb"
+ *
+ * Global header file for AutoGen
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef AUTOGEN_BUILD
+#define AUTOGEN_BUILD 1
+
+#include "compat/unlocked-io.h"
+
+#include REGEX_HEADER
+#include <libguile/scmconfig.h>
+#if GUILE_VERSION < 107000
+# include <guile/gh.h>
+#else
+# include <libguile.h>
+#endif
+
+#include "ag-text.h"
+#include "opts.h"
+#include "expr.h"
+#include "autoopts/autoopts.h"
+#include "directive.h"
+#include "snprintfv/printf.h"
+
+#define LOG10_2to32 10 /* rounded up */
+
+#if defined(SHELL_ENABLED) || defined(DAEMON_ENABLED)
+# ifndef HAVE_WORKING_FORK
+# error SHELL is enabled and fork() does not work
+ choke me
+# endif
+#endif
+
+#ifndef DIRCH
+# if defined(_WIN32) && !defined(__CYGWIN__)
+# define DIRCH '\\'
+# else
+# define DIRCH '/'
+# endif
+#endif
+
+#define YYSTYPE t_word
+
+#define ag_offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
+
+/*
+ * Dual pipe opening of a child process
+ */
+typedef struct {
+ int fd_read;
+ int fd_write;
+} fd_pair_t;
+
+typedef struct {
+ FILE * fp_read; /* parent read fp */
+ FILE * fp_write; /* parent write fp */
+} fp_pair_t;
+
+#define NOPROCESS ((pid_t)-1)
+#define NULLPROCESS ((pid_t)0)
+#define NL '\n'
+#define TAB '\t'
+
+#include "cgi-fsm.h"
+#include "defParse-fsm.h"
+
+typedef union {
+ unsigned char * pzStr;
+ unsigned char ch;
+} def_token_u_t;
+
+#define STATE_TABLE /* set up `atexit' and load Guile */ \
+ _State_( INIT ) /* processing command line options */ \
+ _State_( OPTIONS ) /* Loading guile at option time */ \
+ _State_( GUILE_PRELOAD ) /* Loading value definitions */ \
+ _State_( LOAD_DEFS ) /* Loading library template */ \
+ _State_( LIB_LOAD ) /* Loading primary template */ \
+ _State_( LOAD_TPL ) /* processing templates */ \
+ _State_( EMITTING ) /* loading an included template */ \
+ _State_( INCLUDING ) /* end of processing before exit() */ \
+ _State_( CLEANUP ) /* Clean up code in error response */ \
+ _State_( ABORTING ) /* `exit' has been called */ \
+ _State_( DONE )
+
+#define _State_(n) PROC_STATE_ ## n,
+typedef enum { STATE_TABLE COUNT_PROC_STATE } proc_state_t;
+#undef _State_
+
+#define EXPORT
+
+typedef struct out_stack out_stack_t;
+typedef struct out_spec out_spec_t;
+typedef struct scan_context scan_ctx_t;
+typedef struct def_entry def_ent_t;
+typedef struct macro_desc macro_t;
+typedef struct template_desc templ_t;
+typedef struct for_state for_state_t;
+typedef struct tlib_mark tlib_mark_t;
+
+#define MAX_SUFFIX_LEN 8 /* maximum length of a file name suffix */
+#define MAX_HEREMARK_LEN 64 /* max length of a here mark */
+#define SCRIBBLE_SIZE 256 /* much larger than any short name */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Template Library Layout
+ *
+ * Procedure for loading a template function
+ */
+typedef macro_t * (load_proc_t)(templ_t*, macro_t*, char const** ppzScan);
+typedef load_proc_t * load_proc_p_t;
+
+typedef void (unload_proc_t)(macro_t*);
+typedef unload_proc_t* unload_proc_p_t;
+
+/*
+ * Procedure for handling a template function
+ * during the text emission phase.
+ */
+typedef macro_t* (hdlr_proc_t)(templ_t*, macro_t*);
+typedef hdlr_proc_t* hdlr_proc_p_t;
+
+/*
+ * This must be included after the function prototypes
+ * (the prototypes are used in the generated tables),
+ * but before the macro descriptor structure (the function
+ * enumeration is generated here).
+ */
+#include "functions.h"
+
+#define TEMPLATE_REVISION 1
+#define TEMPLATE_MAGIC_MARKER {{{'A', 'G', 'L', 'B'}}, \
+ TEMPLATE_REVISION, FUNCTION_CKSUM }
+
+struct tlib_mark {
+ union {
+ unsigned char str[4]; /* {'A', 'G', 'L', 'B'} */
+ unsigned int i[1];
+ } tlm_magic;
+ unsigned short tlm_revision; /* TEMPLATE_REVISION */
+ unsigned short tlm_cksum; /* FUNCTION_CKSUM */
+};
+
+/*
+ * Defines for conditional expressions.
+ * The first four are an enumeration that appear in the
+ * low four bits and the next-to-lowest four bits.
+ * "PRIMARY_TYPE" and "SECONDARY_TYPE" are masks for
+ * extracting this enumeration. The rest are flags.
+ */
+#define EMIT_VALUE 0x0000 /* emit value of variable */
+#define EMIT_EXPRESSION 0x0001 /* Emit Scheme result */
+#define EMIT_SHELL 0x0002 /* emit shell output */
+#define EMIT_STRING 0x0003 /* emit content of expr */
+#define EMIT_PRIMARY_TYPE 0x0007
+#define EMIT_SECONDARY_TYPE 0x0070
+#define EMIT_SECONDARY_SHIFT 4
+#define EMIT_IF_ABSENT 0x0100
+#define EMIT_ALWAYS 0x0200 /* emit one of two exprs */
+#define EMIT_FORMATTED 0x0400 /* format, if val present */
+#define EMIT_NO_DEFINE 0x0800 /* don't get defined value */
+
+struct macro_desc {
+ mac_func_t md_code; /* Macro function */
+ int md_line; /* of macro def */
+ int md_end_idx; /* End of block macro */
+ int md_sib_idx; /* Sibling macro (ELIF or SELECT) */
+
+ uintptr_t md_name_off; /* macro name (sometimes) */
+ uintptr_t md_txt_off; /* associated text */
+ uintptr_t md_res; /* some sort of result */
+ void * md_pvt;
+};
+
+struct template_desc {
+ tlib_mark_t td_magic; /* TEMPLATE_MAGIC_MARKER */
+ size_t td_size; /* Structure Size */
+ char * td_scan; /* Next Pointer */
+ int td_mac_ct; /* Count of Macros */
+ char const * td_file; /* Name of template file */
+ char * td_name; /* Defined Macro Name */
+ char * td_text; /* offset of the text */
+ char td_start_mac[MAX_SUFFIX_LEN];
+ char td_end_mac[ MAX_SUFFIX_LEN];
+ macro_t td_macros[1]; /* Array of Macros */
+/* char td_text[...]; * strings at end of macros */
+};
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Name/Value Definitions Layout
+ */
+typedef enum {
+ VALTYP_UNKNOWN = 0,
+ VALTYP_TEXT,
+ VALTYP_BLOCK
+} val_typ_t;
+
+
+#define NO_INDEX ((short)0x80DEAD)
+
+typedef struct def_ctx def_ctx_t;
+struct def_ctx {
+ def_ent_t * dcx_defent; //!< ptr to current def set
+ def_ctx_t * dcx_prev; //!< ptr to previous def set
+};
+
+typedef union {
+ def_ent_t * dvu_entry;
+ char * dvu_text;
+} def_val_u;
+
+struct def_entry {
+ def_ent_t * de_next; //!< next member of same level
+ def_ent_t * de_twin; //!< next member with same name
+ def_ent_t * de_ptwin; //!< previous memb. of level
+ def_ent_t * de_etwin; //!< head of chain to end ptr
+ char * de_name; //!< name of this member
+ long de_index; //!< index among twins
+ def_val_u de_val; //!< string or list of children
+ char * de_file; //!< definition file name
+ int de_line; //!< def file source line
+ val_typ_t de_type; //!< text/block/not defined yet
+};
+
+struct scan_context {
+ scan_ctx_t * scx_next;
+ char * scx_scan;
+ char const * scx_fname;
+ char * scx_data;
+ int scx_line;
+};
+
+struct out_spec {
+ out_spec_t * os_next;
+ char const * os_file_fmt;
+ bool os_dealloc_fmt;
+ char os_sfx[ 1 ];
+};
+
+/**
+ * Output stack handling flags.
+ */
+#define FPF_FREE 0x0001 /*!< free the fp structure */
+#define FPF_UNLINK 0x0002 /*!< unlink file (temp file) */
+#define FPF_NOUNLINK 0x0004 /*!< do not unlink file */
+#define FPF_STATIC_NM 0x0008 /*!< name statically alloced */
+#define FPF_NOCHMOD 0x0010 /*!< do not chmod(2) file */
+#define FPF_TEMPFILE 0x0020 /*!< the file is a temp */
+
+struct out_stack {
+ int stk_flags;
+ out_stack_t * stk_prev;
+ FILE * stk_fp;
+ char const * stk_fname;
+};
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * FOR loop processing state
+ */
+/**
+ * The current state of each active FOR loop.
+ */
+struct for_state {
+ bool for_loading; //!< the FOR macro is getting ready
+ int for_from; //!< the first index of loop
+ int for_to; //!< the last index of loop
+ int for_by; //!< the loop increment (usually 1)
+ int for_index; //!< the current index
+ char * for_sep_str; //!< inter-iteration string (allocated)
+ char * for_name; //!< name of iterator (not allocated)
+ bool for_islast; //!< true for last iteration
+ bool for_isfirst; //!< true for first iteration
+ jmp_buf for_env; //!< long jump buffer (BREAK, CONTINUE)
+};
+
+typedef enum {
+ LOOP_JMP_OKAY = 0,
+ LOOP_JMP_NEXT = 1,
+ LOOP_JMP_BREAK = 2
+} loop_jmp_type_t;
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Invocation of a defined macro processing state
+ */
+typedef struct ivk_info ivk_info_t;
+struct ivk_info {
+ ivk_info_t * ii_prev; //!< previous layer
+ int ii_depth; //!< Invocation nesting depth
+ jmp_buf ii_env; //!< long jump buffer (RETURN)
+ int ii_for_depth; //!< for depth for this invocation
+ int ii_for_alloc; //!< for state buffer allocation count
+ for_state_t * ii_for_data; //!< array of "for" macro states
+};
+#define IVK_INFO_INITIALIZER(_p) { \
+ .ii_prev = (_p), \
+ .ii_depth = curr_ivk_info->ii_depth + 1, \
+ .ii_for_depth = 0, \
+ .ii_for_alloc = 0, \
+ .ii_for_data = NULL \
+ }
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Parsing stuff
+ */
+#define _MkStr(_s) #_s
+#define MK_STR(_s) _MkStr(_s)
+
+#define SCM_EVAL_CONST(_s) \
+ do { static int const line = __LINE__ - 1; \
+ static char const file[] = __FILE__; \
+ static char const * text = _s; \
+ last_scm_cmd = text; \
+ ag_scm_c_eval_string_from_file_line(text, file, line); \
+ } while (false)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * GLOBAL VARIABLES
+ *
+ * General Processing Globals
+ */
+#define ag_pname autogenOptions.pzProgName
+MODE proc_state_t processing_state VALUE( PROC_STATE_INIT );
+MODE unsigned int include_depth VALUE( 0 );
+MODE bool defining_macro VALUE( false );
+MODE templ_t * named_tpls VALUE( NULL );
+MODE char const * oops_pfx VALUE( "" );
+/*
+ * "eval_mac_expr" must be able to return a distinct empty string so that
+ * the "CASE" function can distinguish an empty string due to it being a
+ * value from an empty string due to an absent definition.
+ */
+MODE char const no_def_str[1] VALUE( "" );
+
+/*
+ * Template Processing Globals
+ */
+MODE char const * curr_sfx VALUE( NULL );
+/**
+ * The time to set for the modification times of the output files.
+ */
+MODE time_t outfile_time VALUE( 0 );
+/**
+ * The original time autogen started
+ */
+MODE time_t start_time VALUE( 0 );
+MODE out_stack_t * cur_fpstack VALUE( NULL );
+MODE out_spec_t * output_specs VALUE( NULL );
+MODE jmp_buf abort_jmp_buf;
+MODE ivk_info_t root_ivk_info VALUE( { 0 } );
+MODE ivk_info_t * curr_ivk_info VALUE( &root_ivk_info );
+MODE for_state_t * for_state VALUE( NULL );
+MODE FILE * trace_fp VALUE( NULL );
+/**
+ * temporary file name template
+ */
+MODE char const * pz_temp_tpl VALUE( NULL );
+/**
+ * Length of the template that is the temp directory
+ */
+MODE size_t temp_tpl_dir_len VALUE( 0 );
+/**
+ * dependency file file pointer.
+ */
+MODE FILE* dep_fp VALUE( NULL );
+/**
+ * name of target of rule
+ */
+MODE char const * dep_target VALUE( NULL );
+/**
+ * name of dependency file
+ */
+MODE char const * dep_file VALUE( NULL );
+/**
+ * base name of both source and derived files.
+ * Either "_TList" or "_SList" gets put on the end.
+ */
+MODE char const * pz_targ_base VALUE( NULL );
+/**
+ * The actual list of input (source) files.
+ */
+MODE char const * source_list VALUE( NULL );
+MODE size_t source_size VALUE( 0 );
+MODE size_t source_used VALUE( 0 );
+MODE bool dep_phonies VALUE( false );
+MODE char * cgi_stderr VALUE( NULL );
+
+MODE char const * server_args[2] VALUE( { NULL } );
+MODE char const * shell_program VALUE( MK_STR(CONFIG_SHELL) );
+
+/*
+ * AutoGen definiton and template context
+ *
+ * curr_def_ctx is the current, active list of name/value pairs.
+ * Points to its parent list for full search resolution.
+ *
+ * current_tpl the template (and DEFINE macro) from which
+ * the current set of macros is being extracted.
+ *
+ * These are set in exactly ONE place:
+ * On entry to the dispatch routine (gen_block). Two routines, however,
+ * must restore the values: mFunc_Define and mFunc_For. They are the only
+ * routines that dynamically push name/value pairs on the definition stack.
+ */
+MODE def_ctx_t curr_def_ctx VALUE( { NULL } );
+MODE def_ctx_t root_def_ctx VALUE( { NULL } );
+MODE templ_t * current_tpl VALUE( NULL );
+MODE char const * last_scm_cmd VALUE( NULL );
+#ifdef DAEMON_ENABLED
+/*
+ * When operating as a daemon, autogen can be told to reload
+ * its options the next time it wakes up (send it a SIGHUP).
+ */
+MODE bool redo_opts VALUE( true );
+#endif
+
+/*
+ * Current Macro
+ *
+ * This may be set in exactly three places:
+ * 1. The dispatch routine (gen_block) that steps through
+ * a list of macros
+ * 2. mFunc_If may transfer to one of its 'ELIF' or 'ELSE'
+ * alternation macros
+ * 3. mFunc_Case may transfer to one of its selection clauses.
+ */
+MODE macro_t * cur_macro VALUE( NULL );
+MODE tlib_mark_t const magic_marker VALUE( TEMPLATE_MAGIC_MARKER );
+
+/*
+ * Template Parsing Globals
+ */
+MODE int tpl_line VALUE( 1 );
+MODE scan_ctx_t * base_ctx VALUE( NULL );
+MODE scan_ctx_t * cctx VALUE( NULL );
+MODE scan_ctx_t * end_ctx VALUE( NULL );
+MODE size_t end_mac_len VALUE( 0 );
+MODE char end_mac_mark[8] VALUE( "" );
+MODE size_t st_mac_len VALUE( 0 );
+MODE char st_mac_mark[8] VALUE( "" );
+MODE out_stack_t out_root VALUE({ 0 });
+
+#define name_sep_ch '.'
+
+/*
+ * Definition Parsing Globals
+ */
+MODE char * token_str VALUE( NULL );
+MODE te_dp_event token_code VALUE( DP_EV_INVALID );
+
+MODE int ent_stack_depth VALUE( 0 );
+MODE int ent_stack_sz VALUE( 16 );
+MODE def_ent_t * dft_ent_stack[16] VALUE( { 0 } );
+MODE def_ent_t ** ent_stack VALUE( dft_ent_stack );
+MODE def_ent_t * curr_ent VALUE( NULL );
+
+MODE autogen_exit_code_t exit_code VALUE( AUTOGEN_EXIT_OPTION_ERROR );
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * IF we have fopencookie or funopen, then we also have our own fmemopen
+ */
+#ifdef ENABLE_FMEMOPEN
+extern FILE * ag_fmemopen(void *buf, ssize_t len, char const *mode);
+extern int ag_fmemioctl(FILE * fp, int req, ...);
+#endif
+
+typedef union {
+ const void* cp;
+ void* p;
+} v2c_t;
+MODE v2c_t p2p VALUE( { NULL } );
+
+#ifdef DEBUG_ENABLED
+# define AG_ABEND(s) ag_abend_at(s,__FILE__,__LINE__)
+#else
+# define AG_ABEND(s) ag_abend_at(s)
+#endif
+#define AG_CANT(_op, _wh) \
+ AG_ABEND(aprf(CANNOT_FMT, errno, _op, _wh, strerror(errno)))
+
+#ifdef DEBUG_FSM
+# define DEBUG
+#else
+# undef DEBUG
+#endif
+
+#define AG_ABEND_IN(t,m,s) \
+ STMTS( current_tpl=(t); cur_macro=(m); AG_ABEND(s);)
+
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ "<unknown>"
+# endif
+#endif
+
+/*
+ * Code variations based on the version of Guile:
+ */
+#include "guile-iface.h"
+
+#include "proto.h"
+
+static inline SCM ag_eval(char const * pzStr)
+{
+ SCM res;
+ char const * pzSaveScheme = last_scm_cmd; /* Watch for nested calls */
+ last_scm_cmd = pzStr;
+
+ res = ag_scm_c_eval_string_from_file_line(
+ pzStr, current_tpl->td_file, cur_macro->md_line);
+
+ last_scm_cmd = pzSaveScheme;
+ return res;
+}
+
+/*
+ * Extracted from guile-iface stuff. Seems to be stable since for at least
+ * 1.6.0 through 2.0.0. 1.4.x is thoroughly dead now (May, 2011).
+ */
+#define AG_SCM_DISPLAY(_s) \
+ scm_display(_s, scm_current_output_port())
+
+#define AG_SCM_BOOT_GUILE(_ac, _av, _im) \
+ scm_boot_guile((_ac), (_av), (_im), NULL)
+
+#define AG_SCM_APPLY2(_op, _f, _tst) \
+ scm_apply(_op, _f, scm_cons(_tst, AG_SCM_LISTOFNULL()))
+
+#define AG_SCM_CHAR_P(_c) SCM_CHARP(_c)
+
+/*
+ * Hide dummy functions from complexity measurement tools
+ */
+#define HIDE_FN(_t) _t
+
+#endif /* AUTOGEN_BUILD */
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/autogen.h */
diff --git a/agen5/bootstrap.dir b/agen5/bootstrap.dir
new file mode 100644
index 0000000..4d128ba
--- /dev/null
+++ b/agen5/bootstrap.dir
@@ -0,0 +1,148 @@
+#! /bin/echo This_file_must_be_sourced,_not_executed
+#
+# ----------------------------------------------------------------------
+# agen5/bootstrap.dir --- maintainer's bootstrap script
+#
+# Author: Bruce Korb <bkorb@gnu.org>
+# Time-stamp: "2012-04-14 09:39:36 bkorb"
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+# ----------------------------------------------------------------------
+#
+# This script rebuilds sources not kept in the GIT repository.
+# These files are distributed, so it is not necessary to invoke
+# AutoGen before building AutoGen.
+
+# "make" will invoke this file with the name of the desired output
+# as an argument. We do this here rather than in the make file
+# because some of the rules are complex and we don't want to
+# deal with the dual update problem.
+
+readonly stamp_list=$(echo "stamp-opts${nl}stamp-proto"
+ sed -n '/^make_[a-z_]*[(]/{;s/^make_/stamp-/;s/[()].*//;p;}' \
+ ${srcdir}/mk-stamps.sh)
+
+set_defaults()
+{
+ builddir=$(pwd)
+ srcdir=${srcdir:-$builddir}
+ AGexe=$(command -v autogen)
+ CLexe=$(command -v columns)
+ GDexe=$(command -v getdefs)
+
+ test -x "${AGexe}" -a -x "${GDexe}" -a -x "${CLexe}" || \
+ die "autogen is required"
+
+ export builddir srcdir AGexe CLexe GDexe
+
+ # Make sure we have a default for top build and source.
+ # Some of the templates need this information.
+ #
+ local verdata=$(egrep '^AG_' ${top_srcdir}/VERSION | \
+ sed 's,^\(AG[^=]*\)\(.*\),\1\2 export \1,')
+ eval "set -a;${verdata};set +a"
+
+ # disable any $HOME defaults
+ #
+ HOME=/dev/null
+ SHELL=${POSIX_SHELL-/bin/sh}
+
+ ${VERBOSE:-false} && set -x || :
+ # Ensure complete success or a noticable failure
+ #
+ set -e
+}
+
+assemble_Makefile() {
+ {
+ sed '/# *start-generated-text/q' ${srcdir}/Makefile.am
+ echo
+
+ echo list_stamps = '\'
+ sflist=''
+ for f in $stamp_list
+ do sflist=${sflist}$f${nl}
+ done
+ printf %s "$sflist" | \
+ ${CLexe} --spread=1 -I4 --line=' \'
+
+ echo "if AMDEP"
+ for f in $stamp_list
+ do
+ echo "@am__include@ @am__quote@./\$(DEPDIR)/${f}.d@am__quote@"
+ done
+ echo endif
+
+ fmt='\n%s:\n\t@target="$(AUTOGEN_%s_TList)" \\\n'
+ fmt=${fmt}'\t$(MAKE_STAMP)\n'
+ for f in $stamp_list
+ do
+ g=$(echo $f | sed 's/[^a-zA-Z0-9]/_/g')
+ printf "$fmt" $f $g
+ done
+ printf '\n# end-generated-text\n# end of Makefile.am\n'
+
+ } > Makefile.XX
+
+ if cmp -s Makefile.XX Makefile.am
+ then rm -f Makefile.XX
+ else mv -f Makefile.XX Makefile.am
+ fi
+}
+
+assemble_fmemopen() {
+ fmemdir=~bkorb/tools/mine/lib/fmemopen
+ test -f ${fmemdir}/fmemopen.c || return
+
+ test -f agen5/fmemopen.c && rm -f agen5/fmemopen.c
+ {
+ echo '#if defined(ENABLE_FMEMOPEN)'
+ echo '#include <sys/ioctl.h>'
+ echo
+ sed -n '/^typedef enum/,/_IOWR(/p' ${fmemdir}/libfmem.h
+ sed -e '/^#if defined(HAVE_FOPENCOOKIE)/p' \
+ -e '/=--subblock/,/^#if defined(HAVE_FOPENCOOKIE)/d' \
+ -e "s% @file .*% @file $(realpath ${fmemdir}/fmemopen.c)%" \
+ ${fmemdir}/fmemopen.c
+ echo '#endif /* ENABLE_FMEMOPEN */'
+ } > fmemopen.c
+}
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+#
+# M A I N
+#
+PS4='>bsag-${FUNCNAME}> '
+set_defaults ${1+"$@"}
+DEPDIR=
+. ${srcdir}/mk-stamps.sh $stamp_list
+assemble_fmemopen
+assemble_Makefile
+
+# IF we symlinked in columns or getdefs,
+# THEN it is time to unlink them
+#
+test -z "$rmlist" || rm -f $rmlist
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:4
+# sh-basic-offset:4
+# indent-tabs-mode: nil
+# End:
+
+# end of agen5/bootstrap.dir
diff --git a/agen5/cgi-fsm.c b/agen5/cgi-fsm.c
new file mode 100644
index 0000000..6965d79
--- /dev/null
+++ b/agen5/cgi-fsm.c
@@ -0,0 +1,323 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (cgi-fsm.c)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:35 AM by AutoGen 5.16.2pre7
+ * From the definitions cgi.def
+ * and the template file fsm
+ *
+ * Automated Finite State Machine
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name ``Bruce Korb'' nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * AutoFSM IS PROVIDED BY Bruce Korb ``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 Bruce Korb OR ANY OTHER 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 DEFINE_FSM
+#include "cgi-fsm.h"
+#include <stdio.h>
+
+/*
+ * Do not make changes to this file, except between the START/END
+ * comments, or it will be removed the next time it is generated.
+ */
+/* START === USER HEADERS === DO NOT CHANGE THIS COMMENT */
+
+#include "autogen.h"
+
+/* END === USER HEADERS === DO NOT CHANGE THIS COMMENT */
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/*
+ * Enumeration of the valid transition types
+ * Some transition types may be common to several transitions.
+ */
+typedef enum {
+ CGI_TR_INVALID,
+ CGI_TR_NAME_EQUAL,
+ CGI_TR_SEPARATE,
+ CGI_TR_STASH,
+ CGI_TR_VALUE_ESCAPE
+} te_cgi_trans;
+#define CGI_TRANSITION_CT 5
+
+/**
+ * State transition handling map. Map the state enumeration and the event
+ * enumeration to the new state and the transition enumeration code (in that
+ * order). It is indexed by first the current state and then the event code.
+ */
+typedef struct cgi_transition t_cgi_transition;
+struct cgi_transition {
+ te_cgi_state next_state;
+ te_cgi_trans transition;
+};
+static const t_cgi_transition
+cgi_trans_table[ CGI_STATE_CT ][ CGI_EVENT_CT ] = {
+
+ /* STATE 0: CGI_ST_INIT */
+ { { CGI_ST_NAME, CGI_TR_STASH }, /* EVT: ALPHA */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: NAME_CHAR */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: = */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: + */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: % */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: OTHER */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: & */
+ { CGI_ST_INVALID, CGI_TR_INVALID } /* EVT: END */
+ },
+
+
+ /* STATE 1: CGI_ST_NAME */
+ { { CGI_ST_NAME, CGI_TR_STASH }, /* EVT: ALPHA */
+ { CGI_ST_NAME, CGI_TR_STASH }, /* EVT: NAME_CHAR */
+ { CGI_ST_VALUE, CGI_TR_NAME_EQUAL }, /* EVT: = */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: + */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: % */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: OTHER */
+ { CGI_ST_INVALID, CGI_TR_INVALID }, /* EVT: & */
+ { CGI_ST_INVALID, CGI_TR_INVALID } /* EVT: END */
+ },
+
+
+ /* STATE 2: CGI_ST_VALUE */
+ { { CGI_ST_VALUE, CGI_TR_STASH }, /* EVT: ALPHA */
+ { CGI_ST_VALUE, CGI_TR_STASH }, /* EVT: NAME_CHAR */
+ { CGI_ST_VALUE, CGI_TR_STASH }, /* EVT: = */
+ { CGI_ST_VALUE, CGI_TR_STASH }, /* EVT: + */
+ { CGI_ST_VALUE, CGI_TR_VALUE_ESCAPE }, /* EVT: % */
+ { CGI_ST_VALUE, CGI_TR_STASH }, /* EVT: OTHER */
+ { CGI_ST_INIT, CGI_TR_SEPARATE }, /* EVT: & */
+ { CGI_ST_DONE, CGI_TR_SEPARATE } /* EVT: END */
+ }
+};
+
+
+#define CgiFsmErr_off 19
+#define CgiEvInvalid_off 75
+#define CgiStInit_off 83
+
+
+static char const zCgiStrings[133] =
+/* 0 */ "** OUT-OF-RANGE **\0"
+/* 19 */ "FSM Error: in state %d (%s), event %d (%s) is invalid\n\0"
+/* 75 */ "invalid\0"
+/* 83 */ "init\0"
+/* 88 */ "name\0"
+/* 93 */ "value\0"
+/* 99 */ "alpha\0"
+/* 105 */ "name_char\0"
+/* 115 */ "=\0"
+/* 117 */ "+\0"
+/* 119 */ "%\0"
+/* 121 */ "other\0"
+/* 127 */ "&\0"
+/* 129 */ "end";
+
+static const size_t aszCgiStates[3] = {
+ 83, 88, 93 };
+
+static const size_t aszCgiEvents[9] = {
+ 99, 105, 115, 117, 119, 121, 127, 129, 75 };
+
+
+#define CGI_EVT_NAME(t) ( (((unsigned)(t)) >= 9) \
+ ? zCgiStrings : zCgiStrings + aszCgiEvents[t])
+
+#define CGI_STATE_NAME(s) ( (((unsigned)(s)) >= 3) \
+ ? zCgiStrings : zCgiStrings + aszCgiStates[s])
+
+#ifndef EXIT_FAILURE
+# define EXIT_FAILURE 1
+#endif
+
+static int cgi_invalid_transition( te_cgi_state st, te_cgi_event evt );
+
+/* * * * * * * * * THE CODE STARTS HERE * * * * * * * *
+ *
+ * Print out an invalid transition message and return EXIT_FAILURE
+ */
+static int
+cgi_invalid_transition( te_cgi_state st, te_cgi_event evt )
+{
+ /* START == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */
+ char* pz = aprf( zCgiStrings + CgiFsmErr_off, st, CGI_STATE_NAME( st ),
+ evt, CGI_EVT_NAME( evt ));
+
+ AG_ABEND( aprf(CGI_PARSE_ERR_FMT, pz ));
+ /* END == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */
+
+ return EXIT_FAILURE;
+}
+
+/**
+ * Run the FSM. Will return CGI_ST_DONE or CGI_ST_INVALID
+ */
+te_cgi_state
+cgi_run_fsm(
+ char const* pzSrc,
+ int inlen,
+ char* pzOut,
+ int outlen )
+{
+ te_cgi_state cgi_state = CGI_ST_INIT;
+ te_cgi_event trans_evt;
+ te_cgi_state nxtSt;
+ te_cgi_trans trans;
+ char const* saved_pzSrc = pzSrc;
+ int saved_inlen = inlen;
+ char* saved_pzOut = pzOut;
+ int saved_outlen = outlen;
+ (void)saved_pzSrc;
+ (void)saved_inlen;
+ (void)saved_pzOut;
+ (void)saved_outlen;
+
+ while (cgi_state < CGI_ST_INVALID) {
+
+ /* START == FIND TRANSITION == DO NOT CHANGE THIS COMMENT */
+
+ char curCh;
+ if (--inlen < 0) {
+ trans_evt = CGI_EV_END;
+ curCh = NUL;
+
+ } else {
+ if (outlen < 4) {
+ static char const exhaustion[] = "output space exhausted\n";
+ if (saved_outlen > (int)sizeof(exhaustion))
+ memcpy(saved_pzOut, exhaustion, sizeof(exhaustion));
+
+ return CGI_ST_INVALID;
+ }
+ curCh = *(pzSrc++);
+ if (IS_ALPHABETIC_CHAR( curCh ))
+ trans_evt = CGI_EV_ALPHA;
+ else if (IS_DEC_DIGIT_CHAR( curCh ))
+ trans_evt = CGI_EV_NAME_CHAR;
+ else switch (curCh) {
+ case '_': trans_evt = CGI_EV_NAME_CHAR; break;
+ case '=': trans_evt = CGI_EV_EQUAL; break;
+ case '+': trans_evt = CGI_EV_SPACE; curCh = ' '; break;
+ case '%': trans_evt = CGI_EV_ESCAPE; break;
+ case '&': trans_evt = CGI_EV_SEPARATOR; break;
+ default: trans_evt = CGI_EV_OTHER; break;
+ }
+ }
+
+ /* END == FIND TRANSITION == DO NOT CHANGE THIS COMMENT */
+
+#ifndef __COVERITY__
+ if (trans_evt >= CGI_EV_INVALID) {
+ nxtSt = CGI_ST_INVALID;
+ trans = CGI_TR_INVALID;
+ } else
+#endif /* __COVERITY__ */
+ {
+ const t_cgi_transition* pTT =
+ cgi_trans_table[ cgi_state ] + trans_evt;
+ nxtSt = pTT->next_state;
+ trans = pTT->transition;
+ }
+
+
+ switch (trans) {
+ case CGI_TR_INVALID:
+ /* START == INVALID == DO NOT CHANGE THIS COMMENT */
+ exit( cgi_invalid_transition( cgi_state, trans_evt ));
+ /* END == INVALID == DO NOT CHANGE THIS COMMENT */
+ break;
+
+
+ case CGI_TR_NAME_EQUAL:
+ /* START == NAME_EQUAL == DO NOT CHANGE THIS COMMENT */
+ strcpy( pzOut, "='" );
+ outlen -= 2;
+ pzOut += 2;
+ /* END == NAME_EQUAL == DO NOT CHANGE THIS COMMENT */
+ break;
+
+
+ case CGI_TR_SEPARATE:
+ /* START == SEPARATE == DO NOT CHANGE THIS COMMENT */
+ strcpy( pzOut, "';\n" );
+ outlen -= 2;
+ pzOut += 3;
+ /* END == SEPARATE == DO NOT CHANGE THIS COMMENT */
+ break;
+
+
+ case CGI_TR_STASH:
+ /* START == STASH == DO NOT CHANGE THIS COMMENT */
+ *(pzOut++) = curCh;
+ outlen--;
+ /* END == STASH == DO NOT CHANGE THIS COMMENT */
+ break;
+
+
+ case CGI_TR_VALUE_ESCAPE:
+ /* START == VALUE_ESCAPE == DO NOT CHANGE THIS COMMENT */
+ {
+ char z[4];
+ if (inlen < 2)
+ exit( cgi_invalid_transition( cgi_state, trans_evt ));
+
+ z[0] = *(pzSrc++);
+ z[1] = *(pzSrc++);
+ z[2] = NUL;
+ inlen -= 2;
+
+ /*
+ * We must backslash quote certain characters that are %-quoted
+ * in the input string:
+ */
+ switch (*(pzOut++) = (int)strtol( z, NULL, 16 )) {
+ case '\'':
+ case '\\':
+ case '#':
+ pzOut[0] = pzOut[-1];
+ pzOut[-1] = '\\';
+ pzOut++;
+ }
+ }
+ /* END == VALUE_ESCAPE == DO NOT CHANGE THIS COMMENT */
+ break;
+
+
+ default:
+ /* START == BROKEN MACHINE == DO NOT CHANGE THIS COMMENT */
+ exit( cgi_invalid_transition( cgi_state, trans_evt ));
+ /* END == BROKEN MACHINE == DO NOT CHANGE THIS COMMENT */
+ }
+
+ cgi_state = nxtSt;
+ }
+ return cgi_state;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of cgi-fsm.c */
diff --git a/agen5/cgi-fsm.h b/agen5/cgi-fsm.h
new file mode 100644
index 0000000..4a1cedf
--- /dev/null
+++ b/agen5/cgi-fsm.h
@@ -0,0 +1,91 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (cgi-fsm.h)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:35 AM by AutoGen 5.16.2pre7
+ * From the definitions cgi.def
+ * and the template file fsm
+ *
+ * Automated Finite State Machine
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name ``Bruce Korb'' nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * AutoFSM IS PROVIDED BY Bruce Korb ``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 Bruce Korb OR ANY OTHER CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * This file enumerates the states and transition events for a FSM.
+ *
+ * te_cgi_state
+ * The available states. FSS_INIT is always defined to be zero
+ * and FSS_INVALID and FSS_DONE are always made the last entries.
+ *
+ * te_cgi_event
+ * The transition events. These enumerate the event values used
+ * to select the next state from the current state.
+ * CGI_EV_INVALID is always defined at the end.
+ */
+#ifndef AUTOFSM_CGI_FSM_H_GUARD
+#define AUTOFSM_CGI_FSM_H_GUARD 1
+
+/*
+ * Finite State machine States
+ *
+ * Count of non-terminal states. The generated states INVALID and DONE
+ * are terminal, but INIT is not :-).
+ */
+#define CGI_STATE_CT 3
+typedef enum {
+ CGI_ST_INIT, CGI_ST_NAME, CGI_ST_VALUE, CGI_ST_INVALID,
+ CGI_ST_DONE
+} te_cgi_state;
+
+/*
+ * Finite State machine transition Events.
+ *
+ * Count of the valid transition events
+ */
+#define CGI_EVENT_CT 8
+typedef enum {
+ CGI_EV_ALPHA, CGI_EV_NAME_CHAR, CGI_EV_EQUAL, CGI_EV_SPACE,
+ CGI_EV_ESCAPE, CGI_EV_OTHER, CGI_EV_SEPARATOR, CGI_EV_END,
+ CGI_EV_INVALID
+} te_cgi_event;
+
+/**
+ * Run the FSM. Will return CGI_ST_DONE or CGI_ST_INVALID
+ */
+extern te_cgi_state
+cgi_run_fsm(
+ char const* pzSrc,
+ int inlen,
+ char* pzOut,
+ int outlen );
+
+#endif /* AUTOFSM_CGI_FSM_H_GUARD */
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of cgi-fsm.h */
diff --git a/agen5/cgi.def b/agen5/cgi.def
new file mode 100644
index 0000000..5175bc1
--- /dev/null
+++ b/agen5/cgi.def
@@ -0,0 +1,55 @@
+
+/*
+ * Time-stamp: "2010-06-30 20:53:21 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+autogen definitions fsm;
+
+/*
+ * This FSM is used to process input CGI data into AutoGen definitions
+ * output. There are three basic states: initial, name processing
+ * and processing a value.
+ */
+
+method = case;
+type = loop;
+cookie = "char const* pzSrc";
+cookie = "int inlen";
+cookie = "char* pzOut";
+cookie = "int outlen";
+
+state = name, value;
+event = alpha, name_char, equal, space, escape, other, separator, end;
+
+equal = "=";
+space = "+";
+escape = "%";
+separator = "&";
+
+transition =
+ { tst = init; tev = alpha; next = name; ttype = stash; },
+ { tst = name; tev = alpha, name_char; ttype = stash; },
+ { tst = name; tev = equal; next = value; },
+ { tst = value; tev = '*'; ttype = stash; },
+ { tst = value; tev = space; ttype = stash; },
+ { tst = value; tev = escape; },
+ { tst = value; tev = end; next = done; ttype = separate; },
+ { tst = value; tev = separator; next = init; ttype = separate; };
+
+/* end of agen5/cgi.def */
diff --git a/agen5/defDirect.c b/agen5/defDirect.c
new file mode 100644
index 0000000..5da38e1
--- /dev/null
+++ b/agen5/defDirect.c
@@ -0,0 +1,1048 @@
+/**
+ * @file defDirect.c
+ *
+ * Time-stamp: "2012-03-31 13:59:07 bkorb"
+ *
+ * This module processes definition file directives.
+ *
+ * blocksort spacing=2 \
+ * output=defDirect-sorted.c \
+ * input=defDirect.c \
+ * pat='^/\*=directive' \
+ * start='^doDir_IGNORE' \
+ * trail='\+\+\+ End of Directives'
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define NO_MATCH_ERR(_typ) \
+AG_ABEND(aprf(DIRECT_NOMATCH_FMT, cctx->scx_fname, cctx->scx_line, _typ))
+
+static int ifdefLevel = 0;
+
+static teDirectives
+findDirective(char* pzDirName);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * processDirective
+ *
+ * THIS IS THE ONLY EXTERNAL ENTRY POINT
+ *
+ * A directive character has been found.
+ * Decide what to do and return a pointer to the character
+ * where scanning is to resume.
+ */
+LOCAL char*
+processDirective(char* pzScan)
+{
+ const tDirTable * pTbl = dirTable;
+ char * pzDir;
+ char * pzEnd;
+
+ /*
+ * Search for the end of the #-directive.
+ * Replace "\\\n" sequences with " ".
+ */
+ for (;;) {
+ pzEnd = strchr(pzScan, NL);
+
+ if (pzEnd == NULL) {
+ /*
+ * The end of the directive is the end of the string
+ */
+ pzEnd = pzScan + strlen(pzScan);
+ break;
+ }
+ cctx->scx_line++;
+
+ if (pzEnd[-1] != '\\') {
+ /*
+ * The end of the directive is the end of the line
+ * and the line has not been continued.
+ */
+ *(pzEnd++) = NUL;
+ break;
+ }
+
+ /*
+ * Replace the escape-newline pair with spaces and
+ * find the next end of line
+ */
+ pzEnd[-1] = pzEnd[0] = ' ';
+ }
+
+ /*
+ * Ignore ``#!'' as a comment, enabling a definition file to behave
+ * as a script that gets interpreted by autogen. :-)
+ */
+ if (*pzScan == '!')
+ return pzEnd;
+
+ /*
+ * Find the start of the directive name. Ensure it _is_ a name.
+ */
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+ if (! IS_ALPHABETIC_CHAR(*pzScan))
+ return pzEnd;
+
+ /*
+ * Find the *END* of the directive name.
+ */
+ pzDir = pzScan;
+ pzScan = SPN_ALPHABETIC_CHARS(pzScan+1);
+
+ /*
+ * IF there is anything that follows the name, ...
+ */
+ if (*pzScan != NUL) {
+ char * pz;
+
+ /*
+ * IF something funny immediately follows the directive name,
+ * THEN we will ignore it completely.
+ */
+ if (! IS_WHITESPACE_CHAR(*pzScan))
+ return pzEnd;
+
+ /*
+ * Terminate the name being defined
+ * and find the start of anything else.
+ */
+ *pzScan = NUL;
+ pzScan = SPN_WHITESPACE_CHARS(pzScan+1);
+
+
+ /*
+ * Trim off trailing white space
+ */
+ pz = pzScan + strlen(pzScan);
+ while ( (pz > pzScan)
+ && IS_WHITESPACE_CHAR(pz[-1])) pz--;
+ *pz = NUL;
+ }
+
+ pTbl = dirTable + (int)findDirective(pzDir);
+ return (*(pTbl->pDirProc))(pzScan, pzEnd);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Figure out the index of a directive. We will return the directive
+ * count if it is a bogus name.
+ */
+
+static teDirectives
+findDirective(char* pzDirName)
+{
+ teDirectives res = (teDirectives)0;
+ const tDirTable * pTbl = dirTable;
+
+ do {
+ if (strneqvcmp(pzDirName, pTbl->pzDirName, pTbl->nameSize) != 0)
+ continue;
+
+ /*
+ * A directive name ends with either white space or a NUL
+ */
+ if (IS_END_TOKEN_CHAR(pzDirName[ pTbl->nameSize ]))
+ return res;
+
+ } while (pTbl++, ++res < DIRECTIVE_CT);
+
+ {
+ char ch;
+ if (strlen(pzDirName) > 32) {
+ ch = pzDirName[32];
+ pzDirName[32] = NUL;
+ } else {
+ ch = NUL;
+ }
+
+ fprintf(trace_fp, FIND_DIRECT_UNKNOWN, cctx->scx_fname,
+ cctx->scx_line, pzDirName);
+
+ if (ch != NUL)
+ pzDirName[32] = ch;
+ }
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Support routines for the directives
+ *
+ * skipToEndif
+ *
+ * Skip through the text to a matching "#endif". We do this when we
+ * have processed the allowable text (found an "#else" after
+ * accepting the preceeding text) or when encountering a "#if*def"
+ * while skipping a block of text due to a failed test.
+ */
+static char*
+skipToEndif(char* pzStart)
+{
+ char* pzScan = pzStart;
+ char* pzRet;
+
+ for (;;) {
+ /*
+ * 'pzScan' is pointing to the first character on a line.
+ * Check for a directive on the current line before scanning
+ * later lines.
+ */
+ if (*pzScan == '#')
+ pzScan++;
+ else {
+ char* pz = strstr(pzScan, DIRECT_CK_LIST_MARK);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname,
+ cctx->scx_line));
+
+ pzScan = pz + 2;
+ }
+
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+
+ switch (findDirective(pzScan)) {
+ case DIR_ENDIF:
+ {
+ /*
+ * We found the endif we are interested in
+ */
+ char* pz = strchr(pzScan, NL);
+ if (pz != NULL)
+ pzRet = pz+1;
+ else pzRet = pzScan + strlen(pzScan);
+ goto leave_func;
+ }
+
+ case DIR_IFDEF:
+ case DIR_IFNDEF:
+ /*
+ * We found a nested ifdef/ifndef
+ */
+ pzScan = skipToEndif(pzScan);
+ break;
+
+ default:
+ /*
+ * We do not care what we found
+ */
+ break; /* ignore it */
+ } /* switch (findDirective(pzScan)) */
+ }
+
+ leave_func:
+ while (pzStart < pzRet) {
+ if (*(pzStart++) == NL)
+ cctx->scx_line++;
+ }
+ return pzRet;
+}
+
+
+static char*
+skipToEndmac(char* pzStart)
+{
+ char* pzScan = pzStart;
+ char* pzRet;
+
+ for (;;) {
+ /*
+ * 'pzScan' is pointing to the first character on a line.
+ * Check for a directive on the current line before scanning
+ * later lines.
+ */
+ if (*pzScan == '#')
+ pzScan++;
+ else {
+ char* pz = strstr(pzScan, DIRECT_CK_LIST_MARK);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname,
+ cctx->scx_line));
+
+ pzScan = pz + 2;
+ }
+
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+
+ if (findDirective(pzScan) == DIR_ENDMAC) {
+ /*
+ * We found the endmac we are interested in
+ */
+ char* pz = strchr(pzScan, NL);
+ if (pz != NULL)
+ pzRet = pz+1;
+ else pzRet = pzScan + strlen(pzScan);
+ break;
+ }
+ }
+
+ while (pzStart < pzRet) {
+ if (*(pzStart++) == NL)
+ cctx->scx_line++;
+ }
+ return pzRet;
+}
+
+
+/*
+ * skipToElseEnd
+ *
+ * Skip through the text to a matching "#endif" or "#else" or
+ * "#elif*def". We do this when we are skipping code due to a failed
+ * "#if*def" test.
+ */
+static char*
+skipToElseEnd(char* pzStart)
+{
+ char* pzScan = pzStart;
+ char* pzRet;
+
+ for (;;) {
+ /*
+ * 'pzScan' is pointing to the first character on a line.
+ * Check for a directive on the current line before scanning
+ * later lines.
+ */
+ if (*pzScan == '#')
+ pzScan++;
+ else {
+ char* pz = strstr(pzScan, DIRECT_CK_LIST_MARK);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname,
+ cctx->scx_line));
+
+ pzScan = pz + 2;
+ }
+
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+
+ switch (findDirective(pzScan)) {
+ case DIR_ELSE:
+ /*
+ * We found an "else" directive for an "ifdef"/"ifndef"
+ * that we were skipping over. Start processing the text.
+ */
+ ifdefLevel++;
+ /* FALLTHROUGH */
+
+ case DIR_ENDIF:
+ {
+ /*
+ * We reached the end of the "ifdef"/"ifndef" we were
+ * skipping (or we dropped in from above).
+ * Start processing the text.
+ */
+ char* pz = strchr(pzScan, NL);
+ if (pz != NULL)
+ pzRet = pz+1;
+ else pzRet = pzScan + strlen(pzScan);
+ goto leave_func;
+ }
+
+ case DIR_IFDEF:
+ case DIR_IFNDEF:
+ /*
+ * We have found a nested "ifdef"/"ifndef".
+ * Call "skipToEndif()" to find *its* end, then
+ * resume looking for our own "endif" or "else".
+ */
+ pzScan = skipToEndif(pzScan);
+ break;
+
+ default:
+ /*
+ * We either don't know what it is or we do not care.
+ */
+ break;
+ } /* switch (findDirective(pzScan)) */
+ }
+
+ leave_func:
+ while (pzStart < pzRet) {
+ if (*(pzStart++) == NL)
+ cctx->scx_line++;
+ }
+ return pzRet;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Special routines for each directive. These routines are *ONLY*
+ * called from the table when the input is being processed.
+ * After this routine are either declarations or definitions of
+ * directive handling routines. The documentation for these routines
+ * is extracted from this file. See 'makedef.sh' for how it works.
+ * Each declared directive should have either a 'dummy:' section
+ * (if the directive is to be ignored) or a 'text:' section
+ * (if there is some form of implementation). If the directive
+ * needs or may take arguments (e.g. '#define'), then there should
+ * also be an 'arg:' section describing the argument(s).
+ */
+static char *
+doDir_IGNORE(char * arg, char * scan)
+{
+ (void)arg;
+ return scan;
+}
+
+
+/*=directive assert
+ *
+ * arg: `shell-script` | (scheme-expr) | <anything else>
+ *
+ * text:
+ * If the @code{shell-script} or @code{scheme-expr} do not yield @code{true}
+ * valued results, autogen will be aborted. If @code{<anything else>} or
+ * nothing at all is provided, then this directive is ignored.
+ *
+ * When writing the shell script, remember this is on a preprocessing
+ * line. Multiple lines must be backslash continued and the result is a
+ * single long line. Separate multiple commands with semi-colons.
+ *
+ * The result is @code{false} (and fails) if the result is empty, the
+ * number zero, or a string that starts with the letters 'n' or 'f' ("no"
+ * or "false").
+=*/
+static void
+check_assert_str(char const* pz, char const* pzArg)
+{
+ pz = SPN_WHITESPACE_CHARS(pz);
+
+ if (IS_FALSE_TYPE_CHAR(*pz))
+ AG_ABEND(aprf(DIRECT_ASSERT_FMT, pz, pzArg));
+}
+
+static char*
+doDir_assert(char* pzArg, char* pzScan)
+{
+ switch (*pzArg) {
+ case '`':
+ {
+ char* pzS = pzArg+1;
+ char* pzR;
+
+ pzR = strrchr(pzS, '`');
+ if (pzR == NULL)
+ break; /* not a valid script */
+
+ *pzR = NUL;
+ pzS = shell_cmd((char const*)pzS);
+ check_assert_str(pzS, pzArg);
+ free(pzS);
+ break;
+ }
+
+ case '(':
+ {
+ SCM res = ag_scm_c_eval_string_from_file_line(
+ pzArg, cctx->scx_fname, cctx->scx_line );
+
+ check_assert_str(scm2display(res), pzArg);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return pzScan;
+}
+
+
+/*=directive define
+ *
+ * arg: name [ <text> ]
+ *
+ * text:
+ * Will add the name to the define list as if it were a DEFINE program
+ * argument. Its value will be the first non-whitespace token following
+ * the name. Quotes are @strong{not} processed.
+ *
+ * After the definitions file has been processed, any remaining entries
+ * in the define list will be added to the environment.
+=*/
+static char*
+doDir_define(char* pzArg, char* pzScan)
+{
+ char* pzName = pzArg;
+
+ /*
+ * Skip any #defines that do not look reasonable
+ */
+ if (! IS_VAR_FIRST_CHAR(*pzArg))
+ return pzScan;
+
+ pzArg = SPN_VARIABLE_NAME_CHARS(pzArg);
+
+ /*
+ * IF this is a macro definition (rather than a value def),
+ * THEN we will ignore it.
+ */
+ if (*pzArg == '(')
+ return pzScan;
+
+ /*
+ * We have found the end of the name.
+ * IF there is no more data on the line,
+ * THEN we do not have space for the '=' required by PUTENV.
+ * Therefore, move the name back over the "#define"
+ * directive itself, giving us the space needed.
+ */
+ if (! IS_WHITESPACE_CHAR(*pzArg)) {
+ char* pzS = pzName;
+ char* pzD = (pzName -= 6);
+
+ *pzArg = NUL;
+ while ((*(pzD++) = *(pzS++)) != NUL) ;
+ pzD[-1] = '=';
+ pzD[ 0] = NUL;
+
+ } else {
+ /*
+ * Otherwise, insert the '=' and move any data up against it.
+ * We only accept one name-type, space separated token.
+ * We are not ANSI-C. ;-)
+ */
+ char* pz = pzArg+1;
+ *pzArg++ = '=';
+ pz = SPN_WHITESPACE_CHARS(pz);
+
+ for (;;) {
+ if ((*pzArg++ = *pz++) == NUL)
+ break;
+ if (! IS_UNQUOTABLE_CHAR(*pz)) {
+ *pzArg = NUL;
+ break;
+ }
+ }
+ }
+
+ SET_OPT_DEFINE(pzName);
+ return pzScan;
+}
+
+
+/*=directive elif
+ *
+ * text:
+ * This must follow an @code{#if}
+ * otherwise it will generate an error.
+ * It will be ignored.
+=*/
+static char *
+doDir_elif(char * arg, char * scan)
+{
+ (void)arg;
+ (void)scan;
+ AG_ABEND(aprf(DIRECT_ELIF_BAD_FMT, cctx->scx_fname, cctx->scx_line));
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=directive else
+ *
+ * text:
+ * This must follow an @code{#if}, @code{#ifdef} or @code{#ifndef}.
+ * If it follows the @code{#if}, then it will be ignored. Otherwise,
+ * it will change the processing state to the reverse of what it was.
+=*/
+static char *
+doDir_else(char * arg, char * scan)
+{
+ (void)arg;
+ if (--ifdefLevel < 0)
+ NO_MATCH_ERR(DIRECT_ELSE_BAD);
+
+ return skipToEndif(scan);
+}
+
+
+/*=directive endif
+ *
+ * text:
+ * This must follow an @code{#if}, @code{#ifdef} or @code{#ifndef}.
+ * In all cases, this will resume normal processing of text.
+=*/
+static char *
+doDir_endif(char * arg, char * scan)
+{
+ (void)arg;
+ if (--ifdefLevel < 0)
+ NO_MATCH_ERR(DIRECT_ENDIF_BAD);
+
+ return scan;
+}
+
+
+/*=directive endmac
+ *
+ * text:
+ * This terminates a "macdef", but must not ever be encountered directly.
+=*/
+static char *
+doDir_endmac(char * arg, char * scan)
+{
+ NO_MATCH_ERR(DIRECT_ENDMAC_BAD);
+ (void)arg;
+ (void)scan;
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=directive endshell
+ *
+ * text:
+ * Ends the text processed by a command shell into autogen definitions.
+=*/
+static char *
+doDir_endshell(char * arg, char * scan)
+{
+ /*
+ * In actual practice, the '#endshell's must be consumed inside
+ * the 'doDir_shell()' procedure.
+ */
+ NO_MATCH_ERR(DIRECT_ENDSHELL_BAD);
+ (void)arg;
+ (void)scan;
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=directive error
+ *
+ * arg: [ <descriptive text> ]
+ *
+ * text:
+ * This directive will cause AutoGen to stop processing
+ * and exit with a status of EXIT_FAILURE.
+=*/
+static char *
+doDir_error(char * arg, char * scan)
+{
+ (void)scan;
+
+ AG_ABEND(aprf(DIRECT_ERROR_FMT, cctx->scx_fname, cctx->scx_line, arg));
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=directive ident
+ *
+ * dummy: Ident directives are ignored.
+=*/
+
+
+/*=directive if
+ *
+ * arg: [ <ignored conditional expression> ]
+ *
+ * text:
+ * @code{#if} expressions are not analyzed. @strong{Everything} from here
+ * to the matching @code{#endif} is skipped.
+=*/
+static char *
+doDir_if(char * arg, char * pzScan)
+{
+ (void)arg;
+
+ return skipToEndif(pzScan);
+}
+
+
+/*=directive ifdef
+ *
+ * arg: name-to-test
+ *
+ * text:
+ * The definitions that follow, up to the matching @code{#endif} will be
+ * processed only if there is a corresponding @code{-Dname} command line
+ * option or if a @code{#define} of that name has been previously encountered.
+=*/
+static char *
+doDir_ifdef(char * pzArg, char * pzScan)
+{
+ if (get_define_str(pzArg, false) == NULL)
+ return skipToElseEnd(pzScan);
+ ifdefLevel++;
+ return pzScan;
+}
+
+
+/*=directive ifndef
+ *
+ * arg: name-to-test
+ *
+ * text:
+ * The definitions that follow, up to the matching @code{#endif} will be
+ * processed only if there is @strong{not} a corresponding @code{-Dname}
+ * command line option or there was a canceling @code{-Uname} option.
+=*/
+static char *
+doDir_ifndef(char * pzArg, char * pzScan)
+{
+ if (get_define_str(pzArg, false) != NULL)
+ return skipToElseEnd(pzScan);
+ ifdefLevel++;
+ return pzScan;
+}
+
+
+/*=directive include
+ *
+ * arg: unadorned-file-name
+ *
+ * text:
+ * This directive will insert definitions from another file into
+ * the current collection. If the file name is adorned with
+ * double quotes or angle brackets (as in a C program), then the
+ * include is ignored.
+=*/
+static char*
+doDir_include(char* pzArg, char* pzScan)
+{
+ static char const * const apzSfx[] = { DIRECT_INC_DEF_SFX, NULL };
+ scan_ctx_t* pCtx;
+ size_t inclSize;
+ char zFullName[ AG_PATH_MAX + 1 ];
+
+ /*
+ * Ignore C-style includes. This allows "C" files to be processed
+ * for their "#define"s.
+ */
+ if ((*pzArg == '"') || (*pzArg == '<'))
+ return pzScan;
+ cctx->scx_scan = pzScan;
+
+ if (! SUCCESSFUL(
+ find_file(pzArg, zFullName, apzSfx, cctx->scx_fname))) {
+ fprintf(trace_fp, DIRECT_INC_CANNOT_FIND,
+ pzArg);
+ return pzScan;
+ }
+
+ /*
+ * Make sure the specified file is a regular file and we can get
+ * the correct size for it.
+ */
+ {
+ struct stat stbf;
+ if (stat(zFullName, &stbf) != 0) {
+ fprintf(trace_fp, DIRECT_INC_CANNOT_STAT, errno, strerror(errno),
+ zFullName);
+ return pzScan;
+ }
+ if (! S_ISREG(stbf.st_mode)) {
+ fprintf(trace_fp, DIRECT_INC_NOT_REG, zFullName);
+ return pzScan;
+ }
+ inclSize = stbf.st_size;
+ if (outfile_time < stbf.st_mtime)
+ outfile_time = stbf.st_mtime;
+ }
+
+ if (inclSize == 0)
+ return pzScan;
+
+ /*
+ * Get the space for the output data and for context overhead.
+ * This is an extra allocation and copy, but easier than rewriting
+ * 'loadData()' for this special context.
+ */
+ {
+ size_t sz = sizeof(scan_ctx_t) + 4 + inclSize;
+ pCtx = (scan_ctx_t*)AGALOC(sz, "inc def head");
+
+ memset((void*)pCtx, 0, sz);
+ pCtx->scx_line = 1;
+ }
+
+ /*
+ * Link it into the context stack
+ */
+ pCtx->scx_next = cctx;
+ cctx = pCtx;
+ AGDUPSTR(pCtx->scx_fname, zFullName, "def file");
+
+ pCtx->scx_scan =
+ pCtx->scx_data =
+ pzScan = (char*)(pCtx + 1);
+
+ /*
+ * Read all the data. Usually in a single read, but loop
+ * in case multiple passes are required.
+ */
+ {
+ FILE* fp = fopen(zFullName, "r" FOPEN_TEXT_FLAG);
+ char* pz = pzScan;
+
+ if (fp == NULL)
+ AG_CANT(DIRECT_INC_CANNOT_OPEN, zFullName);
+
+ if (dep_fp != NULL)
+ add_source_file(zFullName);
+
+ do {
+ size_t rdct = fread((void*)pz, (size_t)1, inclSize, fp);
+
+ if (rdct == 0)
+ AG_CANT(DIRECT_INC_CANNOT_READ, zFullName);
+
+ pz += rdct;
+ inclSize -= rdct;
+ } while (inclSize > 0);
+
+ fclose(fp);
+ *pz = NUL;
+ }
+
+ return pzScan;
+}
+
+
+/*=directive let
+ *
+ * dummy: let directives are ignored.
+=*/
+
+
+/*=directive line
+ *
+ * text:
+ *
+ * Alters the current line number and/or file name. You may wish to
+ * use this directive if you extract definition source from other files.
+ * @command{getdefs} uses this mechanism so AutoGen will report the correct
+ * file and approximate line number of any errors found in extracted
+ * definitions.
+=*/
+static char*
+doDir_line(char* pzArg, char* pzScan)
+{
+ /*
+ * The sequence must be: #line <number> "file-name-string"
+ *
+ * Start by scanning up to and extracting the line number.
+ */
+ pzArg = SPN_WHITESPACE_CHARS(pzArg);
+ if (! IS_DEC_DIGIT_CHAR(*pzArg))
+ return pzScan;
+
+ cctx->scx_line = strtol(pzArg, &pzArg, 0);
+
+ /*
+ * Now extract the quoted file name string.
+ * We dup the string so it won't disappear on us.
+ */
+ pzArg = SPN_WHITESPACE_CHARS(pzArg);
+ if (*(pzArg++) != '"')
+ return pzScan;
+ {
+ char* pz = strchr(pzArg, '"');
+ if (pz == NULL)
+ return pzScan;
+ *pz = NUL;
+ }
+
+ AGDUPSTR(cctx->scx_fname, pzArg, "#line");
+
+ return pzScan;
+}
+
+
+/*=directive macdef
+ *
+ * text:
+ * This is a new AT&T research preprocessing directive. Basically, it is
+ * a multi-line #define that may include other preprocessing directives.
+=*/
+static char*
+doDir_macdef(char* arg, char* pzScan)
+{
+ (void)arg;
+
+ return skipToEndmac(pzScan);
+}
+
+
+/*=directive option
+ *
+ * arg: opt-name [ <text> ]
+ *
+ * text:
+ *
+ * This directive will pass the option name and associated text to the
+ * AutoOpts optionLoadLine routine (@pxref{libopts-optionLoadLine}). The
+ * option text may span multiple lines by continuing them with a backslash.
+ * The backslash/newline pair will be replaced with two space characters.
+ * This directive may be used to set a search path for locating template files
+ * For example, this:
+ *
+ * @example
+ * #option templ-dirs $ENVVAR/dirname
+ * @end example
+ * @noindent
+ * will direct autogen to use the @code{ENVVAR} environment variable to find
+ * a directory named @code{dirname} that (may) contain templates. Since these
+ * directories are searched in most recently supplied first order, search
+ * directories supplied in this way will be searched before any supplied on
+ * the command line.
+=*/
+static char*
+doDir_option(char* pzArg, char* pzScan)
+{
+ optionLoadLine(&autogenOptions, pzArg);
+ return pzScan;
+}
+
+
+/*=directive pragma
+ *
+ * dummy: pragma directives are ignored.
+=*/
+
+
+/*=directive shell
+ *
+ * text:
+ * Invokes @code{$SHELL} or @file{/bin/sh} on a script that should
+ * generate AutoGen definitions. It does this using the same server
+ * process that handles the back-quoted @code{`} text.
+ * @strong{CAUTION}@: let not your @code{$SHELL} be @code{csh}.
+=*/
+static char *
+doDir_shell(char * arg, char * pzScan)
+{
+ static size_t const endshell_len = sizeof("\n#endshell") - 1;
+
+ scan_ctx_t * pCtx;
+ char * pzText = pzScan;
+
+ (void)arg;
+
+ /*
+ * The output time will always be the current time.
+ * The dynamic content is always current :)
+ */
+ outfile_time = time(NULL);
+
+ /*
+ * IF there are no data after the '#shell' directive,
+ * THEN we won't write any data
+ * ELSE we have to find the end of the data.
+ */
+ if (strncmp(pzText, DIRECT_SHELL_END_SHELL+1, endshell_len-1) == 0)
+ return pzScan;
+
+ {
+ char* pz = strstr(pzScan, DIRECT_SHELL_END_SHELL);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_SHELL_NOEND, cctx->scx_fname,
+ cctx->scx_line));
+
+ while (pzScan < pz) {
+ if (*(pzScan++) == NL) cctx->scx_line++;
+ }
+
+ *pzScan = NUL;
+ }
+
+ /*
+ * Advance the scan pointer to the next line after '#endshell'
+ * IF there is no such line,
+ * THEN the scan will resume on a zero-length string.
+ */
+ pzScan = strchr(pzScan + endshell_len, NL);
+ if (pzScan == NULL)
+ pzScan = (void*)zNil;
+
+ /*
+ * Save the scan pointer into the current context
+ */
+ cctx->scx_scan = pzScan;
+
+ /*
+ * Run the shell command. The output text becomes the
+ * "file text" that is used for more definitions.
+ */
+ pzText = shell_cmd(pzText);
+ if (pzText == NULL)
+ return pzScan;
+
+ if (*pzText == NUL) {
+ AGFREE(pzText);
+ return pzScan;
+ }
+
+ /*
+ * Get the space for the output data and for context overhead.
+ * This is an extra allocation and copy, but easier than rewriting
+ * 'loadData()' for this special context.
+ */
+ pCtx = (scan_ctx_t*)AGALOC(sizeof(scan_ctx_t) + strlen(pzText) + 4,
+ "shell output");
+
+ /*
+ * Link the new scan data into the context stack
+ */
+ pCtx->scx_next = cctx;
+ cctx = pCtx;
+
+ /*
+ * Set up the rest of the context structure
+ */
+ AGDUPSTR(pCtx->scx_fname, DIRECT_SHELL_COMP_DEFS, DIRECT_SHELL_COMP_DEFS);
+ pCtx->scx_scan =
+ pCtx->scx_data = (char*)(pCtx+1);
+ pCtx->scx_line = 0;
+ strcpy(pCtx->scx_scan, pzText);
+ AGFREE(pzText);
+
+ return pCtx->scx_scan;
+}
+
+
+/*=directive undef
+ *
+ * arg: name-to-undefine
+ *
+ * text:
+ * Will remove any entries from the define list
+ * that match the undef name pattern.
+=*/
+static char*
+doDir_undef(char* pzArg, char* pzScan)
+{
+ SET_OPT_UNDEFINE(pzArg);
+ return pzScan;
+}
+
+
+/*+++ End of Directives +++*/
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/defDirect.c */
diff --git a/agen5/defFind.c b/agen5/defFind.c
new file mode 100644
index 0000000..c99f790
--- /dev/null
+++ b/agen5/defFind.c
@@ -0,0 +1,944 @@
+/**
+ * @file defFind.c
+ *
+ * Time-stamp: "2012-04-07 09:12:06 bkorb"
+ *
+ * This module locates definitions.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * autogen definitions entry list.
+ */
+typedef struct {
+ size_t del_alloc_ct; //!< entry allocation count
+ size_t del_used_ct; //!< entries in actual use
+ def_ent_t ** del_def_ent_ary; //!< pointer to list of entries
+ int del_level; //!< how deep into it we are
+} def_ent_list_t;
+
+typedef struct hash_name_s hash_name_t;
+struct hash_name_s {
+ hash_name_t * hn_next;
+ char hn_str[0];
+};
+
+static hash_name_t ** hash_table = NULL;
+static int hash_table_ct = 0;
+static char zDefinitionName[ AG_PATH_MAX ];
+
+#define ILLFORMEDNAME() \
+ AG_ABEND(aprf(BAD_NAME_FMT, zDefinitionName, \
+ current_tpl->td_file, cur_macro->md_line));
+
+/* = = = START-STATIC-FORWARD = = = */
+static def_ent_t *
+find_by_index(def_ent_t * ent, char * scan);
+
+static void
+add_to_def_list(def_ent_t * ent, def_ent_list_t * del);
+
+static size_t
+bad_def_name(char * pzD, char const * pzS, size_t srcLen);
+
+static def_ent_t *
+find_def(char * name, def_ctx_t * pDefCtx, bool * indexed);
+
+static int
+hash_string(unsigned char const * pz);
+
+static void
+add_string(char const * pz);
+
+static def_ent_t **
+get_def_list(char * name, def_ctx_t * pDefCtx);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * Find a def entry by an index. Valid indexes are:
+ * * empty, meaning the first
+ * * '$', meaning the last
+ * * a decimal, octal or hex number
+ * * an environment variable with a decimal, octal or hex number
+ *
+ * The "twins" of the passed in entry are searched for a matching
+ * "de_index" value.
+ *
+ * @param[in] ent the eldest twin/sibling of the list to search
+ * @param[in] scan the scanning pointer pointing to the first non-white
+ * character after the open bracket.
+ *
+ * @returns a pointer to the matching definition entry, if any.
+ * Otherwise, NULL.
+ */
+static def_ent_t *
+find_by_index(def_ent_t * ent, char * scan)
+{
+ int idx;
+
+ /*
+ * '[]' means the first entry of whatever index number
+ */
+ if (*scan == ']')
+ return ent;
+
+ /*
+ * '[$]' means the last entry of whatever the last one is.
+ * "de_etwin" points to it, or is NULL.
+ */
+ if (*scan == '$') {
+ scan = SPN_WHITESPACE_CHARS(scan + 1);
+ if (*scan != ']')
+ return NULL;
+
+ if (ent->de_etwin != NULL)
+ return ent->de_etwin;
+
+ return ent;
+ }
+
+ /*
+ * '[nn]' means the specified index number
+ */
+ if (IS_DEC_DIGIT_CHAR(*scan)) {
+ char * pz;
+ idx = strtol(scan, &pz, 0);
+
+ /*
+ * Skip over any trailing space and make sure we have a closer
+ */
+ pz = SPN_WHITESPACE_CHARS(pz);
+ if (*pz != ']')
+ return NULL;
+ }
+
+ else {
+ /*
+ * '[XX]' means get the index from our definitions
+ */
+ char * def = scan;
+ char const * val;
+
+ if (! IS_VAR_FIRST_CHAR(*scan))
+ return NULL;
+
+ scan = SPN_VALUE_NAME_CHARS(scan);
+
+ /*
+ * Temporarily remove the character under *scan and
+ * find the corresponding defined value.
+ */
+ {
+ char svch = *scan;
+ *scan = NUL;
+ val = get_define_str(def, true);
+ *scan = svch;
+ }
+
+ /*
+ * Skip over any trailing space and make sure we have a closer
+ */
+ scan = SPN_WHITESPACE_CHARS(scan);
+ if (*scan != ']')
+ return NULL;
+
+ /*
+ * make sure we found a defined value
+ */
+ if ((val == NULL) || (*val == NUL))
+ return NULL;
+
+ idx = strtol(val, &def, 0);
+
+ /*
+ * Make sure we got a legal number
+ */
+ if (*def != NUL)
+ return NULL;
+ }
+
+ /*
+ * Search for the entry with the specified index.
+ */
+ do {
+ if (ent->de_index > idx)
+ return NULL;
+ if (ent->de_index == idx)
+ break;
+ ent = ent->de_twin;
+ } while (ent != NULL);
+
+ return ent;
+}
+
+
+/*
+ * find entry support routines:
+ *
+ * add_to_def_list: place a new definition entry on the end of the
+ * list of found definitions (reallocating list size as needed).
+ */
+static void
+add_to_def_list(def_ent_t * ent, def_ent_list_t * del)
+{
+ if (++(del->del_used_ct) > del->del_alloc_ct) {
+ del->del_alloc_ct += del->del_alloc_ct + 8; /* 8, 24, 56, ... */
+ del->del_def_ent_ary = (def_ent_t**)
+ AGREALOC((void*)del->del_def_ent_ary,
+ del->del_alloc_ct * sizeof(void*), "add find");
+ }
+
+ del->del_def_ent_ary[del->del_used_ct-1] = ent;
+}
+
+static size_t
+bad_def_name(char * pzD, char const * pzS, size_t srcLen)
+{
+ memcpy((void*)pzD, (void*)pzS, srcLen);
+ pzD[srcLen] = NUL;
+ fprintf(trace_fp, BAD_NAME_FMT, pzD,
+ current_tpl->td_file, cur_macro->md_line);
+ return srcLen + 1;
+}
+
+
+/**
+ * remove white space and roughly verify the syntax.
+ * This procedure will consume everything from the source string that
+ * forms a valid AutoGen compound definition name.
+ * We leave legally when:
+ * 1. the state is "CN_NAME_ENDED", AND
+ * 2. We stumble into a character that is not either '[' or name_sep_ch
+ * (always skipping white space).
+ * We start in CN_START.
+ *
+ * @param[out] pzD place to put canonicalized name
+ * @param[in] pzS input non-canonicalized name
+ * @param[in] srcLen length of input text
+ *
+ * @returns the length of un-consumed source text
+ */
+LOCAL int
+canonical_name(char * pzD, char const * pzS, int srcLen)
+{
+ typedef enum {
+ CN_START_NAME = 0, /* must find a name */
+ CN_NAME_ENDED, /* must find '[' or name_sep_ch or we end */
+ CN_INDEX, /* must find name, number, '$' or ']' */
+ CN_INDEX_CLOSE, /* must find ']' */
+ CN_INDEX_ENDED /* must find name_sep_ch or we end */
+ } teConState;
+
+ teConState state = CN_START_NAME;
+
+ char const* pzOri = pzS;
+ char* pzDst = pzD;
+ size_t stLen = srcLen;
+
+ /*
+ * Before anything, skip a leading name_sep_ch as a special hack
+ * to force a current context lookup.
+ */
+ pzS = SPN_WHITESPACE_CHARS(pzS);
+ if (pzOri != pzS) {
+ srcLen -= pzS - pzOri;
+ if (srcLen <= 0)
+ pzS = zNil;
+ }
+
+ if (*pzS == name_sep_ch) {
+ *(pzD++) = name_sep_ch;
+ pzS++;
+ if (--srcLen <= 0)
+ pzS = zNil;
+ }
+
+ nextSegment:
+
+ /*
+ * The next segment may always start with an alpha character,
+ * but an index may also start with a number. The full number
+ * validation will happen in find_by_index().
+ */
+ {
+ char * p = SPN_WHITESPACE_CHARS(pzS);
+ if (p != pzS) {
+ srcLen -= p - pzS;
+ if (srcLen <= 0)
+ pzS = zNil;
+ pzS = p;
+ }
+ }
+
+ switch (state) {
+ case CN_START_NAME:
+ if (! IS_VAR_FIRST_CHAR(*pzS))
+ return bad_def_name(pzDst, pzOri, stLen);
+ state = CN_NAME_ENDED; /* we found the start of our first name */
+ break; /* fall through to name/number consumption code */
+
+ case CN_NAME_ENDED:
+ switch (*pzS++) {
+ case '[':
+ *(pzD++) = '[';
+ state = CN_INDEX;
+ break;
+
+ case '.': case '/':
+ if (pzS[-1] == name_sep_ch) {
+ *(pzD++) = name_sep_ch;
+ state = CN_START_NAME;
+ break;
+ }
+
+ default:
+ /* legal exit -- we have a name already */
+ *pzD = NUL;
+ return srcLen;
+ }
+
+ if (--srcLen <= 0)
+ return bad_def_name(pzDst, pzOri, stLen);
+ goto nextSegment;
+
+ case CN_INDEX:
+ /*
+ * An index. Valid syntaxes are:
+ *
+ * '[' <#define-d name> ']'
+ * '[' <number> ']'
+ * '[' '$' ']'
+ * '[' ']'
+ *
+ * We will check for and handle the last case right here.
+ * The next cycle must find the index closer (']').
+ */
+ state = CN_INDEX_CLOSE;
+
+ /*
+ * Numbers and #define-d names are handled at the end of the switch.
+ * '$' and ']' are handled immediately below.
+ */
+ if (IS_ALPHANUMERIC_CHAR(*pzS))
+ break;
+
+ /*
+ * A solitary '$' is the highest index, whatever that happens to be
+ * We process that right here because down below we only accept
+ * name-type characters and this is not VMS.
+ */
+ if (*pzS == '$') {
+ if (--srcLen < 0)
+ return bad_def_name(pzDst, pzOri, stLen);
+
+ *(pzD++) = *(pzS++);
+ goto nextSegment;
+ }
+ /* FALLTHROUGH */
+
+ case CN_INDEX_CLOSE:
+ /*
+ * Nothing else is okay.
+ */
+ if ((*(pzD++) = *(pzS++)) != ']')
+ return bad_def_name(pzDst, pzOri, stLen);
+
+ if (--srcLen <= 0) {
+ *pzD = NUL;
+ return srcLen;
+ }
+ state = CN_INDEX_ENDED;
+ goto nextSegment;
+
+ case CN_INDEX_ENDED:
+ if ((*pzS != name_sep_ch) || (--srcLen < 0)) {
+ *pzD = NUL;
+ return srcLen;
+ }
+ *(pzD++) = *(pzS++);
+
+ state = CN_START_NAME;
+ goto nextSegment;
+ }
+
+ /*
+ * The next state must be either looking for what comes after the
+ * end of a name, or for the close bracket after an index.
+ * Whatever, the next token must be a name or a number.
+ */
+ assert((state == CN_NAME_ENDED) || (state == CN_INDEX_CLOSE));
+ assert(IS_ALPHANUMERIC_CHAR(*pzS));
+
+ /*
+ * Copy the name/number. We already know the first character is valid.
+ * However, we must *NOT* downcase #define names...
+ */
+ while (IS_VALUE_NAME_CHAR(*pzS)) {
+ char ch = *(pzS++);
+ if ((state != CN_INDEX_CLOSE) && IS_UPPER_CASE_CHAR(ch))
+ *(pzD++) = tolower(ch);
+
+ else switch (ch) { /* force the separator chars to be '_' */
+ case '-':
+ case '^':
+ *(pzD++) = '_';
+ break;
+
+ default:
+ *(pzD++) = ch;
+ }
+
+ if (--srcLen <= 0) {
+ pzS = zNil;
+ break;
+ }
+ }
+
+ goto nextSegment;
+}
+
+
+/**
+ * Find the definition entry for the name passed in. It is okay to
+ * find block entries IFF they are found on the current level. Once
+ * you start traversing up the tree, the macro must be a text macro.
+ * Return an indicator saying if the element has been indexed (so the
+ * caller will not try to traverse the list of twins).
+ *
+ * @param[in] name name to look for
+ * @param[in] pDefCtx definition context
+ * @param[out] indexed whether the name was indexed or not
+ */
+static def_ent_t *
+find_def(char * name, def_ctx_t * pDefCtx, bool * indexed)
+{
+ static int nestingDepth = 0;
+
+ char * brace;
+ char br_ch;
+ def_ent_t * ent;
+ bool dummy;
+ bool noNesting = false;
+
+ /*
+ * IF we are at the start of a search, then canonicalize the name
+ * we are hunting for, copying it to a modifiable buffer, and
+ * initialize the "indexed" boolean to false (we have not found
+ * an index yet).
+ */
+ if (nestingDepth == 0) {
+ canonical_name(zDefinitionName, name, (int)strlen(name));
+ name = zDefinitionName;
+
+ if (indexed != NULL)
+ *indexed = false;
+ else indexed = &dummy;
+
+ if (*name == name_sep_ch) {
+ noNesting = true;
+ name++;
+ }
+ }
+
+ brace = BRK_NAME_SEP_CHARS(name);
+ br_ch = *brace;
+ *brace = NUL;
+
+ if (br_ch == '[') *indexed = true;
+
+ for (;;) {
+ /*
+ * IF we are at the end of the definitions (reached ROOT),
+ * THEN it is time to bail out.
+ */
+ ent = pDefCtx->dcx_defent;
+ if (ent == NULL)
+ return NULL;
+
+ do {
+ /*
+ * IF the name matches
+ * THEN break out of the double loop
+ */
+ if (strcmp(ent->de_name, name) == 0)
+ goto found_def_entry;
+
+ ent = ent->de_next;
+ } while (ent != NULL);
+
+ /*
+ * IF we are nested, then we cannot change the definition level.
+ * So, we did not find anything.
+ */
+ if ((nestingDepth != 0) || noNesting)
+ return NULL;
+
+ /*
+ * Let's go try the definitions at the next higher level.
+ */
+ pDefCtx = pDefCtx->dcx_prev;
+ if (pDefCtx == NULL)
+ return NULL;
+ } found_def_entry:;
+
+ /*
+ * At this point, we have found the entry that matches the supplied name,
+ * up to the '[' or name_sep_ch or NUL character. It *must* be one of
+ * those three characters.
+ */
+ *brace = br_ch;
+
+ switch (br_ch) {
+ case NUL:
+ return ent;
+
+ case '[':
+ /*
+ * We have to find a specific entry in a list.
+ */
+ brace = SPN_WHITESPACE_CHARS(brace + 1);
+
+ ent = find_by_index(ent, brace);
+ if (ent == NULL)
+ return ent;
+
+ /*
+ * We must find the closing brace, or there is an error
+ */
+ brace = strchr(brace, ']');
+ if (brace == NULL)
+ ILLFORMEDNAME();
+
+ /*
+ * What follows the closing brace? IF we are at the end of the
+ * definition, THEN return what we found. However, if there's
+ * another name, then we have to go look that one up, too.
+ */
+ switch (*++brace) {
+ case NUL:
+ return ent;
+
+ case '.': case '/':
+ /*
+ * Which one? One is valid, the other not and it is not known
+ * at compile time.
+ */
+ if (*brace == name_sep_ch) {
+ name = brace + 1;
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ ILLFORMEDNAME();
+ }
+ break;
+
+ case '.': case '/':
+ if (br_ch == name_sep_ch) {
+ /*
+ * Which one? One is valid, the other not and it is not known
+ * at compile time.
+ *
+ * It is a segmented value name. Set the name pointer
+ * to the next segment and search starting from the newly
+ * available set of definitions.
+ */
+ name = brace + 1;
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ ILLFORMEDNAME();
+ }
+
+ /*
+ * We cannot find a member of a non-block type macro definition.
+ */
+ if (ent->de_type != VALTYP_BLOCK)
+ return NULL;
+
+ /*
+ * Loop through all the twins of the entry we found until
+ * we find the entry we want. We ignore twins if we just
+ * used a subscript.
+ */
+ nestingDepth++;
+ {
+ def_ctx_t ctx = { NULL, &curr_def_ctx };
+
+ ctx.dcx_defent = ent->de_val.dvu_entry;
+
+ for (;;) {
+ def_ent_t* res;
+
+ res = find_def(name, &ctx, indexed);
+ if ((res != NULL) || (br_ch == '[')) {
+ nestingDepth--;
+ return res;
+ }
+ ent = ent->de_twin;
+ if (ent == NULL)
+ break;
+ ctx.dcx_defent = ent->de_val.dvu_entry;
+ }
+ }
+
+ nestingDepth--;
+ return NULL;
+}
+
+/*
+ * This makes certain assumptions about the underlying architecture.
+ * Doesn't matter tho. A high collision rate just makes it a teensy
+ * bit slower.
+ */
+static int
+hash_string(unsigned char const * pz)
+{
+ unsigned int res = 0;
+ while (*pz)
+ res ^= (res << 3) ^ *(pz++);
+
+ return res & (hash_table_ct - 1);
+}
+
+static void
+add_string(char const * pz)
+{
+ unsigned char z[SCRIBBLE_SIZE];
+ size_t z_len;
+
+ /*
+ * If there is no hash table, create one.
+ */
+ if (hash_table_ct == 0) {
+ size_t ct = current_tpl->td_mac_ct;
+ if (ct < SCRIBBLE_SIZE)
+ ct = SCRIBBLE_SIZE;
+ else {
+ int bit_ct = 0;
+ while (ct > 1) {
+ bit_ct++;
+ ct >>= 1;
+ }
+ ct = 1 << bit_ct;
+ }
+
+ hash_table_ct = ct;
+ hash_table = malloc(ct * sizeof (*hash_table));
+ memset(hash_table, NUL, ct * sizeof (*hash_table));
+ }
+
+ /*
+ * Save only the last component of the name, sans any index, too.
+ */
+ {
+ char const * p = strrchr(pz, name_sep_ch);
+ if (p != NULL)
+ pz = p + 1;
+ p = strchr(pz, '[');
+
+ if (p != NULL) {
+ z_len = (p - pz) + 1;
+ if (z_len > sizeof(z))
+ return;
+ memcpy(z, pz, z_len - 1);
+ z[z_len - 1] = NUL;
+
+ } else {
+ z_len = strlen(pz) + 1;
+ if (z_len > sizeof(z))
+ return;
+ memcpy(z, pz, z_len);
+ }
+ }
+
+ /*
+ * canonicalize the name
+ */
+ strtransform((char *)z, (char *)z);
+
+ /*
+ * If a new name, insert it in order.
+ */
+ {
+ int ix = hash_string(z);
+
+ hash_name_t ** hptr = &(hash_table[ix]), *new;
+ while (*hptr != NULL) {
+ int cmp = memcmp(z, (*hptr)->hn_str, z_len);
+ if (cmp == 0)
+ return; /* old name */
+
+ if (cmp > 0)
+ break; /* no matching name can be found */
+
+ hptr = &((*hptr)->hn_next);
+ }
+
+ new = AGALOC(sizeof (*new) + z_len, "hn");
+ new->hn_next = *hptr;
+ *hptr = new;
+ memcpy(new->hn_str, z, z_len);
+ }
+}
+
+/**
+ * locate a definition by name.
+ *
+ * @param[in] name the name to find. May be segmented and/or indexed.
+ * @param[out] indexed whether or not the found name is indexed.
+ */
+LOCAL def_ent_t *
+find_def_ent(char * name, bool * indexed)
+{
+ if (HAVE_OPT(USED_DEFINES))
+ add_string(name);
+ return find_def(name, &curr_def_ctx, indexed);
+}
+
+LOCAL void
+print_used_defines(void)
+{
+ if (hash_table_ct == 0)
+ return;
+
+ {
+ int ix = 0;
+
+ FILE * fp = popen(USED_DEFINES_FMT, "w");
+ if (fp == NULL) return;
+
+ while (ix < hash_table_ct) {
+ hash_name_t * hn = hash_table[ix++];
+ while (hn != NULL) {
+ fprintf(fp, USED_DEFINES_LINE_FMT, hn->hn_str);
+ hn = hn->hn_next;
+ }
+ }
+ pclose(fp);
+ }
+}
+
+
+/**
+ * Find the definition entries for the name passed in. It is okay to find
+ * block entries IFF they are found on the current level. Once you start
+ * traversing up the tree, the macro must be a text macro. Return an
+ * indicator saying if the element has been indexed (so the caller will
+ * not try to traverse the list of twins).
+ */
+static def_ent_t **
+get_def_list(char * name, def_ctx_t * pDefCtx)
+{
+ static def_ent_list_t defList = { 0, 0, NULL, 0 };
+
+ char* pcBrace;
+ char breakCh;
+ def_ent_t* ent;
+ bool noNesting = false;
+
+ /*
+ * IF we are at the start of a search, then canonicalize the name
+ * we are hunting for, copying it to a modifiable buffer, and
+ * initialize the "indexed" boolean to false (we have not found
+ * an index yet).
+ */
+ if (defList.del_level == 0) {
+ if (*name == name_sep_ch) {
+ noNesting = true;
+ name = SPN_WHITESPACE_CHARS(name + 1);
+ }
+
+ if (! IS_VAR_FIRST_CHAR(*name)) {
+ strncpy(zDefinitionName, name, sizeof(zDefinitionName) - 1);
+ zDefinitionName[ sizeof(zDefinitionName) - 1] = NUL;
+ ILLFORMEDNAME();
+ }
+
+ canonical_name(zDefinitionName, name, (int)strlen(name));
+ name = zDefinitionName;
+ defList.del_used_ct = 0;
+ }
+
+ pcBrace = BRK_NAME_SEP_CHARS(name);
+ breakCh = *pcBrace;
+ *pcBrace = NUL;
+
+ for (;;) {
+ /*
+ * IF we are at the end of the definitions (reached ROOT),
+ * THEN it is time to bail out.
+ */
+ ent = pDefCtx->dcx_defent;
+ if (ent == NULL) {
+ /*
+ * Make sure we are not nested. Once we start to nest,
+ * then we cannot "change definition levels"
+ */
+ not_found:
+ if (defList.del_level != 0)
+ ILLFORMEDNAME();
+
+ /*
+ * Don't bother returning zero entry list. Just return NULL.
+ */
+ return NULL;
+ }
+
+ do {
+ /*
+ * IF the name matches
+ * THEN go add it, plus all its twins
+ */
+ if (strcmp(ent->de_name, name) == 0)
+ goto found_def_entry;
+
+ ent = ent->de_next;
+ } while (ent != NULL);
+
+ /*
+ * IF we are nested, then we cannot change the definition level.
+ * Just go and return what we have found so far.
+ */
+ if ((defList.del_level != 0) || noNesting)
+ goto returnResult;
+
+ /*
+ * Let's go try the definitions at the next higher level.
+ */
+ pDefCtx = pDefCtx->dcx_prev;
+ if (pDefCtx == NULL)
+ goto not_found;
+ } found_def_entry:;
+
+ /*
+ * At this point, we have found the entry that matches the supplied name,
+ * up to the '[' or name_sep_ch or NUL character. It *must* be one of
+ * those three characters.
+ */
+ *pcBrace = breakCh;
+
+ switch (breakCh) {
+ case NUL:
+ do {
+ add_to_def_list(ent, &defList);
+ ent = ent->de_twin;
+ } while (ent != NULL);
+ goto returnResult;
+
+ case '[':
+ /*
+ * We have to find a specific entry in a list.
+ */
+ pcBrace = SPN_WHITESPACE_CHARS(pcBrace + 1);
+
+ ent = find_by_index(ent, pcBrace);
+ if (ent == NULL)
+ goto returnResult;
+
+ /*
+ * We must find the closing brace, or there is an error
+ */
+ pcBrace = strchr(pcBrace, ']');
+ if (pcBrace == NULL)
+ ILLFORMEDNAME();
+
+ /*
+ * IF we are at the end of the definition,
+ * THEN return what we found
+ */
+ switch (*++pcBrace) {
+ case NUL:
+ goto returnResult;
+
+ case '.': case '/':
+ /*
+ * Which one? One is valid, the other not and it is not known
+ * at compile time.
+ */
+ if (*pcBrace == name_sep_ch)
+ break;
+ /* FALLTHROUGH */
+
+ default:
+ ILLFORMEDNAME();
+ }
+ name = pcBrace + 1;
+ break;
+
+ case '.': case '/':
+ if (breakCh == name_sep_ch) {
+ /*
+ * Which one? One is valid, the other not and it is not known
+ * at compile time.
+ *
+ * It is a segmented value name. Set the name pointer to the
+ * next segment and search starting from the newly available set
+ * of definitions.
+ */
+ name = pcBrace + 1;
+ break;
+ }
+
+ default:
+ ILLFORMEDNAME();
+ }
+
+ /*
+ * Loop through all the twins of the entry. We only look once if we used
+ * a subscript. Ignore any entry types that are not "Blocks" because
+ * text entries won't have any children.
+ */
+ defList.del_level++;
+ do {
+ def_ctx_t ctx = { ent->de_val.dvu_entry, &curr_def_ctx };
+
+ if (ent->de_type == VALTYP_BLOCK)
+ (void)get_def_list(name, &ctx);
+
+ if (breakCh == '[')
+ break;
+
+ ent = ent->de_twin;
+ } while (ent != NULL);
+
+ defList.del_level--;
+
+ returnResult:
+ if (defList.del_level == 0)
+ add_to_def_list(NULL, &defList);
+
+ return defList.del_def_ent_ary;
+}
+
+
+LOCAL def_ent_t **
+find_def_ent_list(char * name)
+{
+ return get_def_list(name, &curr_def_ctx);
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/defFind.c */
diff --git a/agen5/defLex.c b/agen5/defLex.c
new file mode 100644
index 0000000..39c5a25
--- /dev/null
+++ b/agen5/defLex.c
@@ -0,0 +1,678 @@
+
+/**
+ * @file defLex.c
+ *
+ * Time-stamp: "2012-03-31 13:00:08 bkorb"
+ *
+ * This module scans the template variable declarations and passes
+ * tokens back to the parser.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/*
+ * This keyword table must match those found in agParse.y.
+ * You will find them in a %token statement that follows
+ * a comment "Keywords"
+ */
+#define KEYWORD_TABLE \
+ _KW_( AUTOGEN ) \
+ _KW_( DEFINITIONS )
+
+#define _KW_(w) static char const z ## w [] = #w;
+KEYWORD_TABLE
+#undef _KW_
+
+#define _KW_(w) z ## w,
+static char const* apzKeywords[] = { KEYWORD_TABLE };
+#undef _KW_
+
+#define _KW_(w) DP_EV_ ## w,
+te_dp_event aKeywordTkn[] = { KEYWORD_TABLE };
+#undef _KW_
+
+#define KEYWORD_CT (sizeof(apzKeywords) / sizeof(apzKeywords[0]))
+
+#define ERROR (-1)
+#define FINISH (-1)
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+pop_context(void);
+
+static void
+trim_whitespace(void);
+
+static void
+lex_escaped_char(void);
+
+static tSuccess
+lex_backquote(void);
+
+static tSuccess
+lex_comment(void);
+
+static tSuccess
+lex_here_string(void);
+
+static void
+loadScheme(void);
+
+static void
+alist_to_autogen_def(void);
+
+static char*
+gather_name(char * pzScan, te_dp_event * pRetVal);
+
+static char*
+build_here_str(char* scan);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * Pop off an include context and resume from the including file.
+ */
+static void
+pop_context(void)
+{
+ scan_ctx_t* pCX = cctx;
+ cctx = cctx->scx_next;
+ pCX->scx_next = end_ctx;
+ end_ctx = pCX;
+}
+
+static void
+trim_whitespace(void)
+{
+ char * pz = cctx->scx_scan;
+
+ /*
+ * This ensures that any names found previously
+ * are NUL terminated.
+ */
+ if (*pz == NL)
+ cctx->scx_line++;
+ *pz = NUL;
+
+ for (;;) {
+ pz = SPN_HORIZ_WHITE_CHARS(pz+1);
+ if (*pz != NL)
+ break;
+ cctx->scx_line++;
+ }
+
+ cctx->scx_scan = pz;
+}
+
+static void
+lex_escaped_char(void)
+{
+ static int const semi_colon = ';';
+
+ char* pz = strchr(cctx->scx_scan, semi_colon);
+
+ for (;;) {
+ if (pz == NULL) {
+ pz = cctx->scx_scan + strlen(cctx->scx_scan);
+ break;
+ }
+ if (IS_WHITESPACE_CHAR(pz[1])) {
+ *pz = NUL;
+ pz[1] = semi_colon;
+ break;
+ }
+ pz = strchr(pz+1, semi_colon);
+ }
+
+ token_code = DP_EV_STRING;
+ token_str = pz;
+}
+
+static tSuccess
+lex_backquote(void)
+{
+ int line_no = cctx->scx_line;
+ char* pz = ao_string_cook(cctx->scx_scan, &line_no);
+
+ if (pz == NULL)
+ return FAILURE;
+
+ token_str = cctx->scx_scan;
+
+ cctx->scx_scan = pz;
+
+ token_code = DP_EV_STRING;
+ pz = shell_cmd((char const*)token_str);
+ cctx->scx_line = line_no;
+
+ if (pz == NULL)
+ return PROBLEM;
+
+ token_str = pz;
+ return SUCCESS;
+}
+
+static tSuccess
+lex_comment(void)
+{
+ /*
+ * Allow for a comment, C or C++ style
+ */
+ switch (cctx->scx_scan[1]) {
+ case '*':
+ {
+ char* pz = strstr(cctx->scx_scan+2, END_C_COMMENT);
+ if (pz != NULL) {
+ char* p = cctx->scx_scan+1;
+ for (;;) {
+ p = strchr(p+1, NL);
+ if ((p == NULL) || (p > pz))
+ break;
+ cctx->scx_line++;
+ }
+ cctx->scx_scan = pz+2;
+ return SUCCESS;
+ }
+ break;
+ }
+ case '/':
+ {
+ char* pz = strchr(cctx->scx_scan+2, NL);
+ if (pz != NULL) {
+ cctx->scx_scan = pz+1;
+ cctx->scx_line++;
+ return SUCCESS;
+ }
+ break;
+ }
+ }
+
+ return FAILURE;
+}
+
+static tSuccess
+lex_here_string(void)
+{
+ char* pz;
+ if (cctx->scx_scan[1] != '<')
+ return FAILURE;
+
+ pz = build_here_str(cctx->scx_scan + 2);
+ if (pz == NULL) {
+ token_code = DP_EV_INVALID;
+ return PROBLEM;
+ }
+
+ token_code = DP_EV_HERE_STRING;
+ cctx->scx_scan = pz;
+ return SUCCESS;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * LEXICAL SCANNER
+ */
+LOCAL te_dp_event
+yylex(void)
+{
+#define SET_LIT_TKN(t) \
+ token_code = DP_EV_LIT_ ## t; *(cctx->scx_scan++) = NUL;
+
+ token_code = DP_EV_INVALID;
+
+scanAgain:
+ /*
+ * Start the process of locating a token.
+ * We branch here after skipping over a comment
+ * or processing a directive (which may change our context).
+ */
+ if (IS_WHITESPACE_CHAR(*cctx->scx_scan))
+ trim_whitespace();
+
+ switch (*cctx->scx_scan) {
+ case NUL:
+ /*
+ * IF we are not inside an include context,
+ * THEN go finish.
+ */
+ if (cctx->scx_next == NULL)
+ goto lex_done;
+
+ pop_context();
+ goto scanAgain;
+
+ case '#':
+ {
+ extern char * processDirective(char*);
+ char * pz = processDirective(cctx->scx_scan+1);
+ /*
+ * Ensure that the compiler doesn't try to save a copy of
+ * "cctx" in a register. It must be reloaded from memory.
+ */
+ cctx->scx_scan = pz;
+ goto scanAgain;
+ }
+
+ case '{': SET_LIT_TKN(O_BRACE); break;
+ case '=': SET_LIT_TKN(EQ); break;
+ case '}': SET_LIT_TKN(C_BRACE); break;
+ case '[': SET_LIT_TKN(OPEN_BKT); break;
+ case ']': SET_LIT_TKN(CLOSE_BKT); break;
+ case ';': SET_LIT_TKN(SEMI); break;
+ case ',': SET_LIT_TKN(COMMA); break;
+
+ case '\'':
+ case '"':
+ {
+ char* pz = ao_string_cook(cctx->scx_scan, &(cctx->scx_line));
+ if (pz == NULL)
+ goto NUL_error;
+
+ token_str = cctx->scx_scan;
+
+ token_code = DP_EV_STRING;
+ cctx->scx_scan = pz;
+ break;
+ }
+
+ case '<':
+ switch (lex_here_string()) {
+ case SUCCESS: break;
+ case FAILURE: goto BrokenToken;
+ case PROBLEM: return DP_EV_INVALID;
+ }
+ break;
+
+ case '(':
+ loadScheme();
+ break;
+
+ case '\\':
+ if (strncmp(cctx->scx_scan+1, START_SCHEME_LIST, (size_t)2) == 0) {
+ alist_to_autogen_def();
+ goto scanAgain;
+ }
+ lex_escaped_char();
+ break;
+
+ case '`':
+ switch (lex_backquote()) {
+ case FAILURE: goto NUL_error;
+ case PROBLEM: goto scanAgain;
+ case SUCCESS: break;
+ }
+ break;
+
+ case '/':
+ switch (lex_comment()) {
+ case SUCCESS: goto scanAgain;
+ default: break;
+ }
+ /* FALLTHROUGH */ /* to Invalid input char */
+
+ default:
+ BrokenToken:
+ cctx->scx_scan = gather_name(cctx->scx_scan, &token_code);
+ break;
+ } /* switch (*cctx->scx_scan) */
+
+ return token_code;
+
+NUL_error:
+
+ AG_ABEND(aprf(DEF_ERR_FMT, ag_pname, YYLEX_UNENDING_QUOTE,
+ cctx->scx_fname, cctx->scx_line));
+ return DP_EV_INVALID;
+
+lex_done:
+ /*
+ * First time through, return the DP_EV_END token.
+ * Second time through, we really finish.
+ */
+ if (cctx->scx_scan == zNil) {
+ cctx->scx_next = end_ctx;
+ end_ctx = cctx;
+
+ return DP_EV_INVALID;
+ }
+
+ cctx->scx_scan = (char*)zNil;
+ return DP_EV_END;
+#undef SET_LIT_TKN
+}
+
+
+LOCAL void
+yyerror(char* s)
+{
+ char * pz;
+
+ if (strlen(cctx->scx_scan) > 64 )
+ cctx->scx_scan[64] = NUL;
+
+ switch (token_code) {
+ case DP_EV_VAR_NAME:
+ case DP_EV_OTHER_NAME:
+ case DP_EV_STRING:
+ case DP_EV_NUMBER:
+ if (strlen(token_str) > 64 )
+ token_str[64] = NUL;
+
+ pz = aprf(YYLEX_TOKEN_STR, DP_EVT_NAME(token_code), token_str);
+ break;
+
+ default:
+ pz = aprf(YYLEX_DF_STR, DP_EVT_NAME(token_code));
+ }
+ AG_ABEND(aprf(YYLEX_ERR_FMT, s, cctx->scx_fname, cctx->scx_line,
+ pz, cctx->scx_scan));
+}
+
+
+static void
+loadScheme(void)
+{
+ char* pzText = cctx->scx_scan;
+ char* pzEnd = (char*)skip_scheme(pzText, pzText + strlen(pzText));
+ char endCh = *pzEnd;
+ int schemeLen = (pzEnd - pzText);
+ int next_ln;
+ SCM res;
+
+ /*
+ * NUL terminate the Scheme expression, run it, then restore
+ * the NUL-ed character.
+ */
+ if (*pzEnd == NUL)
+ AG_ABEND(aprf(DEF_ERR_FMT, ag_pname, LOAD_SCM_ENDLESS,
+ cctx->scx_fname, cctx->scx_line));
+
+ *pzEnd = NUL;
+ next_ln = cctx->scx_line + count_nl(pzText);
+
+ processing_state = PROC_STATE_GUILE_PRELOAD;
+ res = ag_scm_c_eval_string_from_file_line(
+ pzText, cctx->scx_fname, cctx->scx_line );
+ processing_state = PROC_STATE_LOAD_DEFS;
+ *pzEnd = endCh;
+
+ cctx->scx_scan = pzEnd;
+ pzEnd = (char*)scm2display(res); /* ignore const-ness */
+ cctx->scx_line = next_ln;
+
+ if ((int)strlen(pzEnd) >= schemeLen) {
+ AGDUPSTR(pzEnd, pzEnd, "SCM Result");
+
+ token_str = pzEnd;
+ }
+
+ else {
+ /*
+ * We know the result is smaller than the source. Copy in place.
+ */
+ strcpy(pzText, pzEnd);
+ token_str = pzText;
+ }
+
+ token_code = DP_EV_STRING;
+}
+
+/*
+ * process a single scheme expression, yielding text that gets processed
+ * into AutoGen definitions.
+ */
+static void
+alist_to_autogen_def(void)
+{
+ char* pzText = ++(cctx->scx_scan);
+ char* pzEnd = (char*)skip_scheme(pzText, pzText + strlen(pzText));
+
+ SCM res;
+ size_t res_len;
+ scan_ctx_t* pCtx;
+
+ /*
+ * Wrap the scheme expression with the `alist->autogen-def' function
+ */
+ {
+ char endCh = *pzEnd;
+ *pzEnd = NUL;
+ pzText = aprf(ALIST_TO_AG_WRAP, pzText);
+ *pzEnd = endCh;
+ }
+
+ /*
+ * Run the scheme expression. The result is autogen definition text.
+ */
+ processing_state = PROC_STATE_GUILE_PRELOAD;
+ res = ag_scm_c_eval_string_from_file_line(
+ pzText, cctx->scx_fname, cctx->scx_line );
+
+ /*
+ * The result *must* be a string, or we choke.
+ */
+ if (! AG_SCM_STRING_P(res))
+ AG_ABEND(ALIST_TO_AG_ERR);
+
+ res_len = AG_SCM_STRLEN(res);
+ processing_state = PROC_STATE_LOAD_DEFS;
+ cctx->scx_scan = pzEnd;
+ AGFREE(pzText);
+
+ /*
+ * Now, push the resulting string onto the input stack
+ * and link the new scan data into the context stack
+ */
+ pCtx = (scan_ctx_t*)AGALOC(sizeof(scan_ctx_t) + 4 + res_len, "lex ctx");
+ pCtx->scx_next = cctx;
+ cctx = pCtx;
+
+ /*
+ * Set up the rest of the context structure
+ */
+ AGDUPSTR(pCtx->scx_fname, ALIST_TO_AG_TEXT, "scheme text");
+ pCtx->scx_scan = \
+ pCtx->scx_data = (char*)(pCtx+1);
+ pCtx->scx_line = 0;
+ memcpy((void*)(pCtx->scx_scan), (void*)AG_SCM_CHARS(res), res_len);
+ pCtx->scx_scan[ res_len ] = NUL;
+
+ /*
+ * At this point, the next token will be obtained from the newly
+ * allocated context structure. When empty, input will resume
+ * from the '}' that we left as the next input token in the old
+ * context.
+ */
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * It may be a number, a name, a keyword or garbage.
+ * Figure out which.
+ */
+static char*
+gather_name(char * pzScan, te_dp_event * pRetVal)
+{
+ /*
+ * Check for a number.
+ * Scan it in and advance "pzScan".
+ */
+ if ( IS_DEC_DIGIT_CHAR(*pzScan)
+ || ( (*pzScan == '-')
+ && IS_DEC_DIGIT_CHAR(pzScan[1])
+ ) ) {
+ token_str = pzScan;
+ (void)strtol(pzScan, &pzScan, 0);
+ *pRetVal = DP_EV_NUMBER;
+ return pzScan;
+ }
+
+ if (! IS_UNQUOTABLE_CHAR(*pzScan))
+ AG_ABEND(aprf(ASSEMBLE_NAME_ERR, ag_pname, *pzScan, cctx->scx_fname,
+ cctx->scx_line));
+
+ {
+ char* pz = SPN_VALUE_NAME_CHARS(pzScan);
+
+ if (IS_UNQUOTABLE_CHAR(*pz)) {
+ *pRetVal = DP_EV_OTHER_NAME;
+ pz = SPN_UNQUOTABLE_CHARS(pz+1);
+
+ } else
+ *pRetVal = DP_EV_VAR_NAME;
+
+ /*
+ * Return a NAME token, maybe.
+ * If the name is actually a keyword,
+ * we will return that token code instead.
+ */
+ token_str = pzScan;
+ pzScan = (char*)pz;
+ }
+
+ /*
+ * Now scan the keyword table.
+ */
+ if (*pRetVal == DP_EV_VAR_NAME) {
+ char sv_ch = *pzScan; /* preserve the following character */
+ int kw_ix = 0;
+ *pzScan = NUL; /* NUL terminate the name */
+
+ do {
+ if (streqvcmp(apzKeywords[ kw_ix ], (char*)token_str) == 0) {
+ /*
+ * Return the keyword token code instead of DP_EV_NAME
+ */
+ *pRetVal = aKeywordTkn[ kw_ix ];
+ break;
+ }
+ } while (++kw_ix < (int)KEYWORD_CT);
+
+ *pzScan = sv_ch; /* restore the following character */
+ }
+
+ return pzScan;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * A quoted string has been found.
+ * Find the end of it and compress any escape sequences.
+ */
+static char*
+build_here_str(char* scan)
+{
+ bool no_tabs = false;
+ char zMark[ MAX_HEREMARK_LEN ];
+ size_t markLen = 0;
+ char* dest;
+ int here_str_line;
+
+ /*
+ * See if we are to strip leading tab chars
+ */
+ if (*scan == '-') {
+ no_tabs = true;
+ scan++;
+ }
+
+ /*
+ * Skip white space up to the marker or EOL
+ */
+ scan = SPN_NON_NL_WHITE_CHARS(scan);
+ if (*scan == NL)
+ AG_ABEND(aprf(DEF_ERR_FMT, ag_pname, HERE_MISS_MARK_STR,
+ cctx->scx_fname, cctx->scx_line));
+
+ /*
+ * Copy the marker, noting its length
+ */
+ {
+ char * pz = SPN_VARIABLE_NAME_CHARS(scan);
+ markLen = pz - scan;
+
+ if (markLen == 0)
+ AG_ABEND(aprf(DEF_ERR_FMT, ag_pname, HERE_MISS_MARK_STR,
+ cctx->scx_fname, cctx->scx_line));
+
+ if (markLen >= sizeof(zMark))
+ AG_ABEND(aprf(DEF_ERR_FMT, ag_pname, HERE_MARK_TOO_LONG,
+ cctx->scx_fname, cctx->scx_line));
+
+ memcpy(zMark, scan, markLen);
+ zMark[markLen] = NUL;
+ }
+
+ token_str = dest = scan;
+
+ /*
+ * Skip forward to the EOL after the marker.
+ */
+ scan = strchr(scan, NL);
+ if (scan == NULL)
+ AG_ABEND(aprf(DEF_ERR_FMT, ag_pname, HERE_ENDLESS_STR,
+ cctx->scx_fname, cctx->scx_line));
+
+ /*
+ * And skip the first new line + conditionally skip tabs
+ */
+ here_str_line = cctx->scx_line++;
+ scan++;
+
+ for (;;) {
+ next_line:
+ if (no_tabs) {
+ while (*scan == TAB) ++scan;
+ if ((scan[0] == '\\') && (scan[1] == TAB))
+ ++scan;
+ }
+
+ /*
+ * If we recognize the end mark, break out.
+ */
+ if (! IS_VARIABLE_NAME_CHAR(scan[markLen]))
+ if (strncmp(scan, zMark, markLen) == 0)
+ break;
+
+ for (;;) {
+ switch (*(dest++) = *(scan++)) {
+ case NL:
+ cctx->scx_line++;
+ goto next_line;
+
+ case NUL:
+ AG_ABEND(aprf(DEF_ERR_FMT, ag_pname, HERE_ENDLESS_STR,
+ cctx->scx_fname, here_str_line));
+ }
+ }
+ } /* while strncmp ... */
+
+ /*
+ * dest may still equal token_str, if no data were copied
+ */
+ if (dest > (char*)token_str)
+ dest[-1] = NUL;
+ else dest[0] = NUL;
+
+ return scan + markLen;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/defLex.c */
diff --git a/agen5/defLoad.c b/agen5/defLoad.c
new file mode 100644
index 0000000..bc36317
--- /dev/null
+++ b/agen5/defLoad.c
@@ -0,0 +1,486 @@
+
+/**
+ * @file defLoad.c
+ *
+ * Time-stamp: "2012-03-04 11:02:51 bkorb"
+ *
+ * This module loads the definitions, calls yyparse to decipher them,
+ * and then makes a fixup pass to point all children definitions to
+ * their parent definition.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef enum {
+ INPUT_DONE,
+ INPUT_STDIN,
+ INPUT_FILE
+} def_input_mode_t;
+
+static def_ent_t* pFreeEntryList = NULL;
+static void* pAllocList = NULL;
+
+#define ENTRY_SPACE (4096 - sizeof(void*))
+#define ENTRY_ALLOC_CT (ENTRY_SPACE / sizeof(def_ent_t))
+#define ENTRY_ALLOC_SIZE \
+ ((ENTRY_ALLOC_CT * sizeof(def_ent_t)) + sizeof(void*))
+
+/* = = = START-STATIC-FORWARD = = = */
+static def_ent_t*
+insert_ent(def_ent_t* pDef);
+
+static def_input_mode_t
+ready_def_input(char const ** ppzfile, size_t * psz);
+/* = = = END-STATIC-FORWARD = = = */
+
+LOCAL def_ent_t *
+new_def_ent(void)
+{
+ def_ent_t * pRes = pFreeEntryList;
+
+ if (pRes != NULL) {
+ pFreeEntryList = pRes->de_next;
+
+ } else {
+ int ct = ENTRY_ALLOC_CT-1;
+ void* p = AGALOC(ENTRY_ALLOC_SIZE, "def headers");
+
+ *((void**)p) = pAllocList;
+ pAllocList = p;
+ pRes = pFreeEntryList = (def_ent_t*)((void**)p + 1);
+
+ /*
+ * This is a post-loop test loop. It will cycle one fewer times
+ * than there are 'def_ent_t' structs in the memory we just alloced.
+ */
+ do {
+ def_ent_t* pNxt = pRes+1;
+ pRes->de_next = pNxt;
+
+ /*
+ * When the loop ends, "pRes" will point to the last allocated
+ * structure instance. That is the one we will return.
+ */
+ pRes = pNxt;
+ } while (--ct > 0);
+
+ /*
+ * Unlink the last entry from the chain. The next time this
+ * routine is called, the *FIRST* structure in this list will
+ * be returned.
+ */
+ pRes[-1].de_next = NULL;
+ }
+
+ memset((void*)pRes, 0, sizeof(*pRes));
+ return pRes;
+}
+
+/*
+ * Append a new entry at the end of a sibling (or twin) list.
+ */
+LOCAL void
+print_ent(def_ent_t * pDef)
+{
+ int ix = 32 - (2 * ent_stack_depth);
+ char const * space = PRINT_DEF_SPACES + ((ix < 0) ? 0 : ix);
+
+ char const * vtyp;
+
+ switch (pDef->de_type) {
+ case VALTYP_UNKNOWN: vtyp = DEF_TYPE_UNKNOWN; break;
+ case VALTYP_TEXT: vtyp = DEF_TYPE_TEXT; break;
+ case VALTYP_BLOCK: vtyp = DEF_TYPE_BLOCK; break;
+ default: vtyp = DEF_TYPE_INVALID; break;
+ }
+
+ fprintf(trace_fp, PRINT_DEF_SHOW_FMT,
+ space,
+ pDef->de_name, (unsigned int)pDef->de_index,
+ vtyp,
+ pDef->de_file, pDef->de_line);
+}
+
+/**
+ * Append a new entry into a sibling (or twin) list.
+ *
+ * @param pDef new definition
+ * @returns usually, the input, but sometimes it is necessary to move
+ * the data, so returns the address of the incoming data regardless.
+ */
+static def_ent_t*
+insert_ent(def_ent_t* pDef)
+{
+ def_ent_t* pList = ent_stack[ ent_stack_depth ];
+
+ /*
+ * If the current level is empty, then just insert this one and quit.
+ */
+ if (pList->de_val.dvu_entry == NULL) {
+ if (pDef->de_index == NO_INDEX)
+ pDef->de_index = 0;
+ pList->de_val.dvu_entry = pDef;
+
+ return pDef;
+ }
+ pList = pList->de_val.dvu_entry;
+
+ /*
+ * Scan the list looking for a "twin" (same-named entry).
+ */
+ while (strcmp(pDef->de_name, pList->de_name) != 0) {
+ /*
+ * IF we are at the end of the list,
+ * THEN put the new entry at the end of the list.
+ * This is a new name in the current context.
+ * The value type is forced to be the same type.
+ */
+ if (pList->de_next == NULL) {
+ pList->de_next = pDef;
+
+ if (pDef->de_index == NO_INDEX)
+ pDef->de_index = 0;
+
+ return pDef;
+ }
+
+ /*
+ * Check the next sibling for a twin value.
+ */
+ pList = pList->de_next;
+ }
+
+ /* * * * * * WE HAVE FOUND A TWIN
+ *
+ * Link in the new twin chain entry into the list.
+ */
+ if (pDef->de_index == NO_INDEX) {
+ def_ent_t* pT = pList->de_etwin;
+ if (pT == NULL)
+ pT = pList;
+
+ pDef->de_index = pT->de_index + 1;
+ pT->de_twin = pDef;
+ pDef->de_ptwin = pT;
+ pList->de_etwin = pDef;
+
+ } else if (pList->de_index > pDef->de_index) {
+
+ /*
+ * Insert the new entry before any other in the list.
+ * We actually do this by leaving the pList pointer alone and swapping
+ * the contents of the definition entry.
+ */
+ def_ent_t def = *pDef;
+
+ memcpy(&(pDef->de_name), &(pList->de_name),
+ sizeof(def) - ag_offsetof(def_ent_t, de_name));
+
+ memcpy(&(pList->de_name), &(def.de_name),
+ sizeof(def) - ag_offsetof(def_ent_t, de_name));
+
+ /*
+ * Contents are swapped. Link "pDef" after "pList" and return "pList".
+ */
+ pDef->de_twin = pList->de_twin;
+ if (pDef->de_twin != NULL)
+ pDef->de_twin->de_ptwin = pDef;
+
+ pDef->de_ptwin = pList;
+ pList->de_twin = pDef;
+
+ /*
+ * IF this is the first twin, then the original list head is now
+ * the "end twin".
+ */
+ if (pList->de_etwin == NULL)
+ pList->de_etwin = pDef;
+
+ pDef = pList; /* Return the replacement structure address */
+
+ } else {
+ def_ent_t* pL = pList;
+ def_ent_t* pT = pL->de_twin;
+
+ /*
+ * Insert someplace after the first entry. Scan the list until
+ * we either find a larger index or we get to the end.
+ */
+ while ((pT != NULL) && (pT->de_index < pDef->de_index)) {
+ pL = pT;
+ pT = pT->de_twin;
+ }
+
+ pDef->de_twin = pT;
+
+ pL->de_twin = pDef;
+ pDef->de_ptwin = pL;
+ if (pT == NULL)
+ pList->de_etwin = pDef;
+ else
+ pT->de_ptwin = pDef;
+ }
+
+ return pDef; /* sometimes will change */
+}
+
+/**
+ * Figure out where to insert an entry in a list of twins.
+ */
+LOCAL def_ent_t *
+number_and_insert_ent(char * name, char const * idx_str)
+{
+ def_ent_t * ent = new_def_ent();
+
+ ent->de_name = name;
+
+ if (idx_str == NULL)
+ ent->de_index = NO_INDEX;
+
+ else if (IS_SIGNED_NUMBER_CHAR(*idx_str))
+ ent->de_index = strtol(idx_str, NULL, 0);
+
+ else {
+ idx_str = get_define_str(idx_str, true);
+ if (idx_str != NULL)
+ ent->de_index = strtol(idx_str, NULL, 0);
+ else ent->de_index = NO_INDEX;
+ }
+
+ strtransform(ent->de_name, ent->de_name);
+ ent->de_type = VALTYP_UNKNOWN;
+ ent->de_file = (char*)cctx->scx_fname;
+ ent->de_line = cctx->scx_line;
+ return (curr_ent = insert_ent(ent));
+}
+
+/**
+ * figure out which file descriptor to use for reading definitions.
+ */
+static def_input_mode_t
+ready_def_input(char const ** ppzfile, size_t * psz)
+{
+ struct stat stbf;
+
+ if (! ENABLED_OPT(DEFINITIONS)) {
+ base_ctx = (scan_ctx_t*)AGALOC(sizeof(scan_ctx_t), "scan context");
+ memset((void*)base_ctx, 0, sizeof(scan_ctx_t));
+ base_ctx->scx_line = 1;
+ base_ctx->scx_fname = READY_INPUT_NODEF;
+
+ if (! ENABLED_OPT(SOURCE_TIME))
+ outfile_time = time(NULL);
+ return INPUT_DONE;
+ }
+
+ *ppzfile = OPT_ARG(DEFINITIONS);
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_DEF_LOAD);
+
+ /*
+ * Check for stdin as the input file. We use the current time
+ * as the modification time for stdin. We also note it so we
+ * do not try to open it and we try to allocate more memory if
+ * the stdin input exceeds our initial allocation of 16K.
+ */
+ if (strcmp(*ppzfile, "-") == 0) {
+ *ppzfile = OPT_ARG(DEFINITIONS) = "stdin";
+ if (getenv(REQUEST_METHOD) != NULL) {
+ loadCgi();
+ cctx = base_ctx;
+ dp_run_fsm();
+ return INPUT_DONE;
+ }
+
+ accept_fifo:
+ outfile_time = time(NULL);
+ *psz = 0x4000 - (4+sizeof(*base_ctx));
+ return INPUT_STDIN;
+ }
+
+ /*
+ * This, then, must be a regular file. Make sure of that and
+ * find out how big it was and when it was last modified.
+ */
+ if (stat(*ppzfile, &stbf) != 0)
+ AG_CANT(READY_INPUT_STAT, *ppzfile);
+
+ if (! S_ISREG(stbf.st_mode)) {
+ if (S_ISFIFO(stbf.st_mode))
+ goto accept_fifo;
+
+ errno = EINVAL;
+ AG_CANT(READY_INPUT_NOT_REG, *ppzfile);
+ }
+
+ /*
+ * IF the source-time option has been enabled, then
+ * our output file mod time will start as one second after
+ * the mod time on this file. If any of the template files
+ * are more recent, then it will be adjusted.
+ */
+ *psz = stbf.st_size;
+
+ outfile_time = ENABLED_OPT(SOURCE_TIME) ? stbf.st_mtime : time(NULL);
+
+ return INPUT_FILE;
+}
+
+/**
+ * Suck in the entire definitions file and parse it.
+ */
+LOCAL void
+read_defs(void)
+{
+ char const * pzDefFile;
+ char * pzData;
+ size_t dataSize;
+ size_t sizeLeft;
+ FILE * fp;
+ def_input_mode_t in_mode = ready_def_input(&pzDefFile, &dataSize);
+
+ if (in_mode == INPUT_DONE)
+ return;
+
+ /*
+ * Allocate the space we need for our definitions.
+ */
+ sizeLeft = dataSize+4+sizeof(*base_ctx);
+ base_ctx = (scan_ctx_t*)AGALOC(sizeLeft, "file buf");
+ memset((void*)base_ctx, 0, sizeLeft);
+ base_ctx->scx_line = 1;
+ sizeLeft = dataSize;
+
+ /*
+ * Our base context will have its currency pointer set to this
+ * input. It is also a scanning pointer, but since this buffer
+ * is never deallocated, we do not have to remember the initial
+ * value. (It may get reallocated here in this routine, tho...)
+ */
+ pzData =
+ base_ctx->scx_scan =
+ base_ctx->scx_data = (char*)(base_ctx+1);
+ base_ctx->scx_next = NULL;
+
+ /*
+ * Set the input file pointer, as needed
+ */
+ if (in_mode == INPUT_STDIN)
+ fp = stdin;
+
+ else {
+ fp = fopen(pzDefFile, "r" FOPEN_TEXT_FLAG);
+ if (fp == NULL)
+ AG_CANT(READ_DEF_OPEN, pzDefFile);
+
+ if (dep_fp != NULL)
+ add_source_file(pzDefFile);
+ }
+
+ /*
+ * Read until done...
+ */
+ for (;;) {
+ size_t rdct = fread((void*)pzData, (size_t)1, sizeLeft, fp);
+
+ /*
+ * IF we are done,
+ */
+ if (rdct == 0) {
+ /*
+ * IF it is because we are at EOF, then break out
+ * ELSE abend.
+ */
+ if (feof(fp) || (in_mode == INPUT_STDIN))
+ break;
+
+ AG_CANT(READ_DEF_READ, pzDefFile);
+ }
+
+ /*
+ * Advance input pointer, decrease remaining count
+ */
+ pzData += rdct;
+ sizeLeft -= rdct;
+
+ /*
+ * See if there is any space left
+ */
+ if (sizeLeft == 0) {
+ scan_ctx_t* p;
+ off_t dataOff;
+
+ /*
+ * IF it is a regular file, then we are done
+ */
+ if (in_mode != INPUT_STDIN)
+ break;
+
+ /*
+ * We have more data and we are out of space.
+ * Try to reallocate our input buffer.
+ */
+ dataSize += (sizeLeft = 0x1000);
+ dataOff = pzData - base_ctx->scx_data;
+ p = AGREALOC((void*)base_ctx, dataSize+4+sizeof(*base_ctx),
+ "expand f buf");
+
+ /*
+ * The buffer may have moved. Set the data pointer at an
+ * offset within the new buffer and make sure our base pointer
+ * has been corrected as well.
+ */
+ if (p != base_ctx) {
+ p->scx_scan = \
+ p->scx_data = (char*)(p+1);
+ pzData = p->scx_data + dataOff;
+ base_ctx = p;
+ }
+ }
+ }
+
+ if (pzData == base_ctx->scx_data)
+ AG_ABEND(READ_DEF_NO_DEFS);
+
+ *pzData = NUL;
+ AGDUPSTR(base_ctx->scx_fname, pzDefFile, "def file name");
+
+ /*
+ * Close the input file, parse the data
+ * and alphabetically sort the definition tree contents.
+ */
+ if (in_mode != INPUT_STDIN)
+ fclose(fp);
+
+ cctx = base_ctx;
+ dp_run_fsm();
+}
+
+
+LOCAL void
+unload_defs(void)
+{
+ return;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/defLoad.c */
diff --git a/agen5/defParse-fsm.c b/agen5/defParse-fsm.c
new file mode 100644
index 0000000..5e5e006
--- /dev/null
+++ b/agen5/defParse-fsm.c
@@ -0,0 +1,389 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (defParse-fsm.c)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:34 AM by AutoGen 5.16.2pre7
+ * From the definitions defParse.def
+ * and the template file fsm
+ *
+ * Automated Finite State Machine
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name ``Bruce Korb'' nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * AutoFSM IS PROVIDED BY Bruce Korb ``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 Bruce Korb OR ANY OTHER 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 DEFINE_FSM
+#include "defParse-fsm.h"
+#include <stdio.h>
+
+#define FSM_USER_HEADERS
+#include "defParse.x"
+#undef FSM_USER_HEADERS
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/**
+ * Callback routine prototype. They return the next state. Normally, that
+ * should be the value of the "maybe_next" argument.
+ */
+typedef te_dp_state (dp_callback_t)(
+ te_dp_state initial,
+ te_dp_state maybe_next,
+ te_dp_event trans_evt );
+
+static dp_callback_t
+ dp_do_empty_val,
+ dp_do_end_block,
+ dp_do_have_name_lit_eq,
+ dp_do_indexed_name,
+ dp_do_invalid,
+ dp_do_need_name_end,
+ dp_do_need_name_var_name,
+ dp_do_next_val,
+ dp_do_start_block,
+ dp_do_str_value,
+ dp_do_tpl_name;
+
+/**
+ * Declare all the state transition handling routines.
+ */
+typedef struct transition t_dp_transition;
+struct transition {
+ te_dp_state next_state;
+ dp_callback_t* trans_proc;
+};
+
+/**
+ * State transition maps. Map the enumeration and the event enumeration
+ * to the new state and the transition enumeration code (in that order).
+ * It is indexed by first the current state and then the event code.
+ */
+static const t_dp_transition
+dp_trans_table[ DP_STATE_CT ][ DP_EVENT_CT ] = {
+
+ /* STATE 0: DP_ST_INIT */
+ { { DP_ST_NEED_DEF, NULL }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 1: DP_ST_NEED_DEF */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_NEED_TPL, NULL }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 2: DP_ST_NEED_TPL */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_NEED_SEMI, dp_do_tpl_name }, /* EVT: VAR_NAME */
+ { DP_ST_NEED_SEMI, dp_do_tpl_name }, /* EVT: OTHER_NAME */
+ { DP_ST_NEED_SEMI, dp_do_tpl_name }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 3: DP_ST_NEED_SEMI */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_NEED_NAME, NULL }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 4: DP_ST_NEED_NAME */
+ { { DP_ST_NEED_DEF, NULL }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_DONE, dp_do_need_name_end }, /* EVT: End-Of-File */
+ { DP_ST_HAVE_NAME, dp_do_need_name_var_name }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_HAVE_VALUE, dp_do_end_block }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 5: DP_ST_HAVE_NAME */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_NEED_NAME, dp_do_empty_val }, /* EVT: ; */
+ { DP_ST_NEED_VALUE, dp_do_have_name_lit_eq }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_NEED_IDX, NULL }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 6: DP_ST_NEED_VALUE */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_HAVE_VALUE, dp_do_str_value }, /* EVT: VAR_NAME */
+ { DP_ST_HAVE_VALUE, dp_do_str_value }, /* EVT: OTHER_NAME */
+ { DP_ST_HAVE_VALUE, dp_do_str_value }, /* EVT: STRING */
+ { DP_ST_HAVE_VALUE, dp_do_str_value }, /* EVT: HERE_STRING */
+ { DP_ST_HAVE_VALUE, dp_do_str_value }, /* EVT: NUMBER */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_NEED_NAME, dp_do_start_block }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 7: DP_ST_NEED_IDX */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_NEED_CBKT, dp_do_indexed_name }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_NEED_CBKT, dp_do_indexed_name }, /* EVT: NUMBER */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 8: DP_ST_NEED_CBKT */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INDX_NAME, NULL } /* EVT: ] */
+ },
+
+ /* STATE 9: DP_ST_INDX_NAME */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_NEED_NAME, dp_do_empty_val }, /* EVT: ; */
+ { DP_ST_NEED_VALUE, NULL }, /* EVT: = */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ },
+
+ /* STATE 10: DP_ST_HAVE_VALUE */
+ { { DP_ST_INVALID, dp_do_invalid }, /* EVT: AUTOGEN */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: DEFINITIONS */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: End-Of-File */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: VAR_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: OTHER_NAME */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: HERE_STRING */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: NUMBER */
+ { DP_ST_NEED_NAME, NULL }, /* EVT: ; */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: = */
+ { DP_ST_NEED_VALUE, dp_do_next_val }, /* EVT: , */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: { */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: } */
+ { DP_ST_INVALID, dp_do_invalid }, /* EVT: [ */
+ { DP_ST_INVALID, dp_do_invalid } /* EVT: ] */
+ }
+};
+
+
+#define DpFsmErr_off 19
+#define DpEvInvalid_off 75
+#define DpStInit_off 83
+
+
+static char const zDpStrings[279] =
+/* 0 */ "** OUT-OF-RANGE **\0"
+/* 19 */ "FSM Error: in state %d (%s), event %d (%s) is invalid\n\0"
+/* 75 */ "invalid\0"
+/* 83 */ "init\0"
+/* 88 */ "need_def\0"
+/* 97 */ "need_tpl\0"
+/* 106 */ "need_semi\0"
+/* 116 */ "need_name\0"
+/* 126 */ "have_name\0"
+/* 136 */ "need_value\0"
+/* 147 */ "need_idx\0"
+/* 156 */ "need_cbkt\0"
+/* 166 */ "indx_name\0"
+/* 176 */ "have_value\0"
+/* 187 */ "autogen\0"
+/* 195 */ "definitions\0"
+/* 207 */ "End-Of-File\0"
+/* 219 */ "var_name\0"
+/* 228 */ "other_name\0"
+/* 239 */ "string\0"
+/* 246 */ "here_string\0"
+/* 258 */ "number\0"
+/* 265 */ ";\0"
+/* 267 */ "=\0"
+/* 269 */ ",\0"
+/* 271 */ "{\0"
+/* 273 */ "}\0"
+/* 275 */ "[\0"
+/* 277 */ "]";
+
+static const size_t aszDpStates[11] = {
+ 83, 88, 97, 106, 116, 126, 136, 147, 156, 166, 176 };
+
+static const size_t aszDpEvents[16] = {
+ 187, 195, 207, 219, 228, 239, 246, 258, 265, 267, 269, 271, 273, 275, 277,
+ 75 };
+
+
+#define DP_EVT_NAME(t) ( (((unsigned)(t)) >= 16) \
+ ? zDpStrings : zDpStrings + aszDpEvents[t])
+
+#define DP_STATE_NAME(s) ( (((unsigned)(s)) >= 11) \
+ ? zDpStrings : zDpStrings + aszDpStates[s])
+
+#ifndef EXIT_FAILURE
+# define EXIT_FAILURE 1
+#endif
+
+static int dp_invalid_transition( te_dp_state st, te_dp_event evt );
+
+#define FSM_HANDLER_CODE
+#include "defParse.x"
+#undef FSM_HANDLER_CODE
+
+/**
+ * Run the FSM. Will return DP_ST_DONE or DP_ST_INVALID
+ */
+te_dp_state
+dp_run_fsm( void )
+{
+ te_dp_state dp_state = DP_ST_INIT;
+ te_dp_event trans_evt;
+ te_dp_state nxtSt;
+ dp_callback_t* pT;
+
+ while (dp_state < DP_ST_INVALID) {
+
+#define FSM_FIND_TRANSITION
+#include "defParse.x"
+#undef FSM_FIND_TRANSITION
+
+#ifndef __COVERITY__
+ if (trans_evt >= DP_EV_INVALID) {
+ nxtSt = DP_ST_INVALID;
+ pT = dp_do_invalid;
+ } else
+#endif /* __COVERITY__ */
+ {
+ const t_dp_transition* pTT =
+ dp_trans_table[ dp_state ] + trans_evt;
+ nxtSt = pTT->next_state;
+ pT = pTT->trans_proc;
+ }
+
+ if (pT != NULL)
+ nxtSt = (*pT)( dp_state, nxtSt, trans_evt );
+
+ dp_state = nxtSt;
+ }
+ return dp_state;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of defParse-fsm.c */
diff --git a/agen5/defParse-fsm.h b/agen5/defParse-fsm.h
new file mode 100644
index 0000000..bdda896
--- /dev/null
+++ b/agen5/defParse-fsm.h
@@ -0,0 +1,92 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (defParse-fsm.h)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:34 AM by AutoGen 5.16.2pre7
+ * From the definitions defParse.def
+ * and the template file fsm
+ *
+ * Automated Finite State Machine
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name ``Bruce Korb'' nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * AutoFSM IS PROVIDED BY Bruce Korb ``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 Bruce Korb OR ANY OTHER CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * This file enumerates the states and transition events for a FSM.
+ *
+ * te_dp_state
+ * The available states. FSS_INIT is always defined to be zero
+ * and FSS_INVALID and FSS_DONE are always made the last entries.
+ *
+ * te_dp_event
+ * The transition events. These enumerate the event values used
+ * to select the next state from the current state.
+ * DP_EV_INVALID is always defined at the end.
+ */
+#ifndef AUTOFSM_DEFPARSE_FSM_H_GUARD
+#define AUTOFSM_DEFPARSE_FSM_H_GUARD 1
+
+/*
+ * Finite State machine States
+ *
+ * Count of non-terminal states. The generated states INVALID and DONE
+ * are terminal, but INIT is not :-).
+ */
+#define DP_STATE_CT 11
+typedef enum {
+ DP_ST_INIT, DP_ST_NEED_DEF, DP_ST_NEED_TPL, DP_ST_NEED_SEMI,
+ DP_ST_NEED_NAME, DP_ST_HAVE_NAME, DP_ST_NEED_VALUE, DP_ST_NEED_IDX,
+ DP_ST_NEED_CBKT, DP_ST_INDX_NAME, DP_ST_HAVE_VALUE, DP_ST_INVALID,
+ DP_ST_DONE
+} te_dp_state;
+
+/*
+ * Finite State machine transition Events.
+ *
+ * Count of the valid transition events
+ */
+#define DP_EVENT_CT 15
+typedef enum {
+ DP_EV_AUTOGEN, DP_EV_DEFINITIONS, DP_EV_END,
+ DP_EV_VAR_NAME, DP_EV_OTHER_NAME, DP_EV_STRING,
+ DP_EV_HERE_STRING, DP_EV_NUMBER, DP_EV_LIT_SEMI,
+ DP_EV_LIT_EQ, DP_EV_LIT_COMMA, DP_EV_LIT_O_BRACE,
+ DP_EV_LIT_C_BRACE, DP_EV_LIT_OPEN_BKT, DP_EV_LIT_CLOSE_BKT,
+ DP_EV_INVALID
+} te_dp_event;
+
+/**
+ * Run the FSM. Will return DP_ST_DONE or DP_ST_INVALID
+ */
+extern te_dp_state
+dp_run_fsm( void );
+
+#endif /* AUTOFSM_DEFPARSE_FSM_H_GUARD */
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of defParse-fsm.h */
diff --git a/agen5/defParse.def b/agen5/defParse.def
new file mode 100644
index 0000000..6b24380
--- /dev/null
+++ b/agen5/defParse.def
@@ -0,0 +1,109 @@
+/*
+ * Time-stamp: "2010-08-06 08:57:49 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+autogen definitions fsm;
+
+method = callout;
+type = loop;
+prefix = dp;
+#ifdef DEBUG_ENABLED
+debug-flag = FSM_DEBUG_ENABLED;
+#endif
+handler-file = defParse.x;
+
+/*
+ * Non-terminal "events"
+ */
+event = "autogen", "definitions", end,
+ var_name, other_name, string, here_string, number;
+
+/*
+ * Terminal token-events
+ */
+event = lit_semi, lit_eq, lit_comma, lit_o_brace, lit_c_brace,
+ lit_open_bkt, lit_close_bkt;
+
+lit-semi = ';';
+lit-eq = '=';
+lit-comma = ',';
+lit-o_brace = '{';
+lit-c_brace = '}';
+lit-open_bkt = '[';
+lit-close_bkt = ']';
+end = 'End-Of-File';
+
+state = need_def, need_tpl, need_semi, need_name,
+ have_name, need_value, need_idx, need_cbkt,
+ indx_name, have_value;
+
+/*
+ * Initial to operational state
+ */
+transition =
+{ tst = init; tev = "autogen"; next = need_def; ttype = noop; },
+{ tst = need_def; tev = "definitions"; next = need_tpl; ttype = noop; },
+{ tst = need_tpl; tev = string, other_name, var_name;
+ next = need_semi; ttype = tpl_name; },
+{ tst = need_semi; tev = lit_semi; next = need_name; ttype = noop; };
+
+/*
+ * When looking for the next named object, you either get the name,
+ * or end a block or end the input. If you get an "autogen" token, ignore it.
+ * We likely got it due to a #include.
+ */
+transition =
+{ tst = need_name; tev = "autogen"; next = need_def; ttype = noop; },
+{ tst = need_name; tev = var_name; next = have_name; },
+{ tst = need_name; tev = lit_c_brace; next = have_value;
+ ttype = end_block; },
+{ tst = need_name; tev = end; next = done; };
+
+/*
+ * We have a name now. Next is a semicolon for an empty string value,
+ * or an '=' to start a value list or a '[' for an indexed value.
+ */
+transition =
+{ tst = have_name; tev = lit_semi; next = need_name;
+ ttype = empty_val; },
+{ tst = have_name; tev = lit_eq; next = need_value; },
+{ tst = have_name; tev = lit_open_bkt; next = need_idx; ttype = noop; },
+{ tst = need_idx; tev = number, var_name;
+ ttype = indexed_name; next = need_cbkt; },
+{ tst = need_cbkt; tev = lit_close_bkt; next = indx_name;
+ ttype = noop; },
+/*
+ * once you have one index, you cannot have another.
+ * Otherwise, "indx_name" is identical to "have_name".
+ */
+{ tst = indx_name; tev = lit_semi; next = need_name;
+ ttype = empty_val; },
+{ tst = indx_name; tev = lit_eq; next = need_value;
+ ttype = noop; };
+
+transition =
+{ tst = need_value; tev = string, here_string, other_name, var_name, number;
+ next = have_value; ttype = str_value; },
+{ tst = need_value; tev = lit_o_brace; next = need_name;
+ ttype = start_block; },
+{ tst = have_value; tev = lit_semi; next = need_name;
+ ttype = noop; },
+{ tst = have_value; tev = lit_comma; next = need_value;
+ ttype = next_val; };
+/* end of agen5/defParse.def */
diff --git a/agen5/defParse.x b/agen5/defParse.x
new file mode 100644
index 0000000..8317960
--- /dev/null
+++ b/agen5/defParse.x
@@ -0,0 +1,245 @@
+
+/**
+ * @file defParse.x
+ *
+ * Definition parser functions.
+ *
+ * Time-stamp: "2012-03-31 13:30:28 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef FSM_USER_HEADERS
+static char* pz_new_name = NULL;
+#endif /* FSM_USER_HEADERS */
+
+#ifdef FSM_FIND_TRANSITION
+trans_evt = yylex();
+#endif /* FSM_FIND_TRANSITION */
+
+#ifdef FSM_HANDLER_CODE
+/*
+ * Print out an invalid transition message and return EXIT_FAILURE
+ */
+static int
+dp_invalid_transition(te_dp_state st, te_dp_event evt)
+{
+ char const * fmt_pz = zDpStrings + DpFsmErr_off;
+ fprintf(stderr, fmt_pz, st, DP_STATE_NAME(st), evt, DP_EVT_NAME(evt));
+
+ return EXIT_FAILURE;
+}
+
+static te_dp_state
+dp_do_empty_val(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ /*
+ * Our state is either "have-name" or "indx-name" and we found a ';',
+ * end of statement. It is a string value with an empty string.
+ */
+ def_ent_t * pDE = number_and_insert_ent(pz_new_name, NULL);
+
+ (void)initial; (void)trans_evt;
+
+ pDE->de_val.dvu_text = (char *)zNil;
+ pDE->de_type = VALTYP_TEXT;
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ print_ent(pDE);
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_end_block(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ if (ent_stack_depth <= 0)
+ yyerror((void*)"Too many close braces");
+
+ (void)initial; (void)trans_evt;
+
+ curr_ent = ent_stack[ ent_stack_depth-- ];
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_have_name_lit_eq(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ /*
+ * Create a new entry but defer "makeMacro" call until we have the
+ * assigned value.
+ */
+ number_and_insert_ent(pz_new_name, NULL);
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_indexed_name(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ /*
+ * Create a new entry with a specified indes, but defer "makeMacro" call
+ * until we have the assigned value.
+ */
+ number_and_insert_ent(pz_new_name, token_str);
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_invalid(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)maybe_next;
+ dp_invalid_transition(initial, trans_evt);
+ yyerror((void*)"invalid transition");
+ /* NOTREACHED */
+ return DP_ST_INVALID;
+}
+
+static te_dp_state
+dp_do_need_name_end(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ if (ent_stack_depth != 0)
+ yyerror((void*)"definition blocks were left open");
+
+ /*
+ * We won't be using the parse stack any more.
+ * We only process definitions once.
+ */
+ if (ent_stack != dft_ent_stack)
+ AGFREE(ent_stack);
+
+ /*
+ * The seed has now done its job. The real root of the
+ * definitions is the first entry off of the seed.
+ */
+ root_def_ctx.dcx_defent = root_def_ctx.dcx_defent->de_val.dvu_entry;
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_need_name_var_name(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ pz_new_name = token_str;
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_next_val(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ /*
+ * Clone the entry name of the current entry.
+ */
+ number_and_insert_ent(curr_ent->de_name, NULL);
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_start_block(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ if (curr_ent->de_type == VALTYP_TEXT)
+ yyerror((void*)"assigning a block value to text name");
+ curr_ent->de_type = VALTYP_BLOCK; /* in case not yet determined */
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ print_ent(curr_ent);
+
+ if (++ent_stack_depth >= ent_stack_sz) {
+ def_ent_t** ppDE;
+ ent_stack_sz += ent_stack_sz / 2;
+
+ if (ent_stack == dft_ent_stack) {
+ ppDE = AGALOC(ent_stack_sz * sizeof(void*), "def stack");
+ memcpy(ppDE, dft_ent_stack, sizeof(dft_ent_stack));
+ } else {
+ ppDE = AGREALOC(ent_stack, ent_stack_sz * sizeof(void*),
+ "stretch def stack");
+ }
+ ent_stack = ppDE;
+ }
+ ent_stack[ ent_stack_depth ] = curr_ent;
+ curr_ent = NULL;
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_str_value(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ if (curr_ent->de_type == VALTYP_BLOCK)
+ yyerror((void*)"assigning a block value to text name");
+
+ curr_ent->de_val.dvu_text = token_str;
+ curr_ent->de_type = VALTYP_TEXT;
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ print_ent(curr_ent);
+
+ /*
+ * The "here string" marker is the line before where the text starts.
+ */
+ if (trans_evt == DP_EV_HERE_STRING)
+ curr_ent->de_line++;
+ return maybe_next;
+}
+
+static te_dp_state
+dp_do_tpl_name(te_dp_state initial, te_dp_state maybe_next,
+ te_dp_event trans_evt)
+{
+ (void)initial; (void)trans_evt;
+ /*
+ * Allow this routine to be called multiple times.
+ * This may happen if we include another definition file.
+ */
+ if (root_def_ctx.dcx_defent == NULL) {
+ static char const zBogus[] = "@BOGUS@";
+ static def_ent_t seed = {
+ NULL, NULL, NULL, NULL, (char*)zBogus, 0, { NULL },
+ (char*)zBogus, 0, VALTYP_BLOCK };
+
+ root_def_ctx.dcx_defent = &seed;
+
+ if (! HAVE_OPT(OVERRIDE_TPL))
+ tpl_fname = token_str;
+
+ ent_stack_depth = 0;
+ ent_stack[0] = &seed;
+ }
+ return maybe_next;
+}
+#endif /* FSM_HANDLER_CODE */
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/defParse.x */
diff --git a/agen5/directive.h b/agen5/directive.h
new file mode 100644
index 0000000..a521a3a
--- /dev/null
+++ b/agen5/directive.h
@@ -0,0 +1,125 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (directive.h)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:39 AM by AutoGen 5.16.2pre7
+ * From the definitions directive.def
+ * and the template file directive
+ *
+ * copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef AUTOGEN_DIRECTIVE_H_GUARD
+#define AUTOGEN_DIRECTIVE_H_GUARD 1
+#ifdef DEFINING
+
+typedef char* (tDirProc)( char* pzArg, char* pzScan );
+
+typedef struct dir_table tDirTable;
+struct dir_table {
+ size_t nameSize;
+ char const * pzDirName;
+ tDirProc * pDirProc;
+ int unused;
+};
+
+/*
+ * Declare the procedures that will handle the directives.
+ */
+static tDirProc doDir_IGNORE;
+static tDirProc doDir_assert;
+static tDirProc doDir_define;
+static tDirProc doDir_elif;
+static tDirProc doDir_else;
+static tDirProc doDir_endif;
+static tDirProc doDir_endmac;
+static tDirProc doDir_endshell;
+static tDirProc doDir_error;
+#define doDir_ident doDir_IGNORE
+static tDirProc doDir_if;
+static tDirProc doDir_ifdef;
+static tDirProc doDir_ifndef;
+static tDirProc doDir_include;
+#define doDir_let doDir_IGNORE
+static tDirProc doDir_line;
+static tDirProc doDir_macdef;
+static tDirProc doDir_option;
+#define doDir_pragma doDir_IGNORE
+static tDirProc doDir_shell;
+static tDirProc doDir_undef;
+
+/*
+ * Define the constant string names for each directive.
+ * We supply all the needed terminating NULs, so tell the compiler
+ * the size to allocate.
+ */
+static char const zDirectives[124] =
+ "assert\0" "define\0" "elif\0" "else\0" "endif\0"
+ "endmac\0" "endshell\0" "error\0" "ident\0" "if\0"
+ "ifdef\0" "ifndef\0" "include\0" "let\0" "line\0"
+ "macdef\0" "option\0" "pragma\0" "shell\0" "undef\0";
+
+/*
+ * Enumerate the directives
+ */
+typedef enum {
+ DIR_ASSERT, DIR_DEFINE, DIR_ELIF, DIR_ELSE, DIR_ENDIF,
+ DIR_ENDMAC, DIR_ENDSHELL, DIR_ERROR, DIR_IDENT, DIR_IF,
+ DIR_IFDEF, DIR_IFNDEF, DIR_INCLUDE, DIR_LET, DIR_LINE,
+ DIR_MACDEF, DIR_OPTION, DIR_PRAGMA, DIR_SHELL, DIR_UNDEF
+} teDirectives;
+
+/*
+ * Set up the table for handling each directive.
+ */
+#define DIRECTIVE_CT 20
+static tDirTable dirTable[ DIRECTIVE_CT ] = {
+ { 6, zDirectives + 0, doDir_assert, 0 },
+ { 6, zDirectives + 7, doDir_define, 0 },
+ { 4, zDirectives + 14, doDir_elif, 0 },
+ { 4, zDirectives + 19, doDir_else, 0 },
+ { 5, zDirectives + 24, doDir_endif, 0 },
+ { 6, zDirectives + 30, doDir_endmac, 0 },
+ { 8, zDirectives + 37, doDir_endshell, 0 },
+ { 5, zDirectives + 46, doDir_error, 0 },
+ { 5, zDirectives + 52, doDir_ident, 0 },
+ { 2, zDirectives + 58, doDir_if, 0 },
+ { 5, zDirectives + 61, doDir_ifdef, 0 },
+ { 6, zDirectives + 67, doDir_ifndef, 0 },
+ { 7, zDirectives + 74, doDir_include, 0 },
+ { 3, zDirectives + 82, doDir_let, 0 },
+ { 4, zDirectives + 86, doDir_line, 0 },
+ { 6, zDirectives + 91, doDir_macdef, 0 },
+ { 6, zDirectives + 98, doDir_option, 0 },
+ { 6, zDirectives +105, doDir_pragma, 0 },
+ { 5, zDirectives +112, doDir_shell, 0 },
+ { 5, zDirectives +118, doDir_undef, 0 } };
+
+#ifdef DAEMON_ENABLED
+typedef struct inet_family_map_s {
+ char const* pz_name;
+ unsigned short nm_len;
+ unsigned short family;
+} inet_family_map_t;
+
+#define INET_FAMILY_TYPE_CT 0
+inet_family_map_t inet_family_map[ 1 ] = {
+ { NULL, 0, 0 } };
+
+#endif /* DAEMON_ENABLED */
+#endif /* DEFINING */
+#endif /* AUTOGEN_DIRECTIVE_H_GUARD */
+/*
+ * End of directive.h */
diff --git a/agen5/directive.tpl b/agen5/directive.tpl
new file mode 100644
index 0000000..8a5429c
--- /dev/null
+++ b/agen5/directive.tpl
@@ -0,0 +1,141 @@
+[= AutoGen5 template -*- Mode: C -*-
+
+# Time-stamp: "2012-01-15 08:39:12 bkorb"
+
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+h =]
+[=
+
+ (define ix 0)
+ (define tmp-txt "")
+ (define dir-tbl "")
+ (define dir-enm "_EOF_\n")
+ (define dir-nms "_EOF_\n")
+
+ (string-append
+ (dne " * " "/* ")
+ "\n *"
+ "\n * copyright (c) 1992-2012 by Bruce Korb - all rights reserved"
+ "\n *\n"
+ (gpl "AutoGen" " * ")
+ "\n */\n"
+ (make-header-guard "autogen")
+ )
+
+=]
+#ifdef DEFINING
+
+typedef char* (tDirProc)( char* pzArg, char* pzScan );
+
+typedef struct dir_table tDirTable;
+struct dir_table {
+ size_t nameSize;
+ char const * pzDirName;
+ tDirProc * pDirProc;
+ int unused;
+};
+
+/*
+ * Declare the procedures that will handle the directives.
+ */
+static tDirProc doDir_IGNORE;[=
+FOR directive =][=
+
+ (set! tmp-txt (get "name"))
+ (set! dir-tbl (string-append dir-tbl
+ (sprintf " { %2d, zDirectives +%3d, doDir_%-10s 0 }"
+ (string-length tmp-txt) ix
+ (string-downcase! (string-append (get "name") ",")) )
+ (if (last-for?) " };" ",\n")
+ ) )
+
+ (set! dir-enm (string-append dir-enm
+ "DIR_" (string-upcase! (get "name")) "\n" ))
+
+ (set! dir-nms (string-append dir-nms
+ " \"" tmp-txt "\\0\"\n" ))
+
+ (set! ix (+ ix (string-length tmp-txt) 1))
+
+=][=
+
+ IF (not (exist? "dummy")) =]
+static tDirProc doDir_[=name=];[=
+ ELSE =]
+#define doDir_[=name=] doDir_IGNORE[=
+ ENDIF =][=
+ENDFOR directive =]
+
+/*
+ * Define the constant string names for each directive.
+ * We supply all the needed terminating NULs, so tell the compiler
+ * the size to allocate.
+ */
+static char const zDirectives[[=(. ix)=]] =
+[= (shellf "columns --spread=1 -I3 <<%s_EOF_" dir-nms) =];
+
+/*
+ * Enumerate the directives
+ */
+typedef enum {
+[= (shellf "columns -I4 -S, --spread=1 <<%s_EOF_" dir-enm) =]
+} teDirectives;
+
+/*
+ * Set up the table for handling each directive.
+ */
+#define DIRECTIVE_CT [= (+ (high-lim "directive") 1) =]
+static tDirTable dirTable[ DIRECTIVE_CT ] = {
+[= (. dir-tbl) =]
+
+#ifdef DAEMON_ENABLED
+typedef struct inet_family_map_s {
+ char const* pz_name;
+ unsigned short nm_len;
+ unsigned short family;
+} inet_family_map_t;
+
+[= `
+
+list=\`find /usr/include -follow -name socket.h | \
+ xargs egrep '^#define[ \t]+AF_[A-Z0-9]+[ \t]+[0-9]' | \
+ sed 's,^.*#define[ \t]*AF_,,;s,[ \t].*,,;/^MAX$/d'\`
+
+set -- $list
+echo "#define INET_FAMILY_TYPE_CT $#"
+echo "inet_family_map_t inet_family_map[ \`expr $# + 1\` ] = {"
+
+for f
+do
+ g=\`echo $f | tr '[A-Z]' '[a-z]'\`':'
+ ct=\`echo $g | wc -c\`
+ printf " { %-14s %3d, AF_${f} },\\n" "\\\"${g}\\\"," ${ct}
+done | sort -u
+
+`=]
+ { NULL, 0, 0 } };
+
+#endif /* DAEMON_ENABLED */
+#endif /* DEFINING */
+#endif /* [=(. header-guard)=] */
+/*
+ * End of [= (out-name) =] */[= #
+
+end of directive.tpl =]
diff --git a/agen5/expExtract.c b/agen5/expExtract.c
new file mode 100644
index 0000000..f2aa7a4
--- /dev/null
+++ b/agen5/expExtract.c
@@ -0,0 +1,360 @@
+/**
+ * @file expExtract.c
+ *
+ * Time-stamp: "2012-03-04 09:16:59 bkorb"
+ *
+ * This module implements a file extraction function.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static char const*
+load_extract_file(char const* pzNewFile);
+
+static SCM
+mk_empty_text(char const* pzStart, char const* pzEnd, SCM def);
+
+static SCM
+get_text(char const* pzText, char const* pzStart, char const* pzEnd, SCM def);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * Load a file into memory. Keep it in memory and try to reuse it
+ * if we get called again. Likely, there will be several extractions
+ * from a single file.
+ */
+static char const*
+load_extract_file(char const* pzNewFile)
+{
+ static char const * pzFile = NULL;
+ static char const * pzText = NULL;
+ struct stat sbuf;
+ char* pzIn;
+
+ /*
+ * Make sure that we:
+ *
+ * o got the file name from the SCM value
+ * o return the old text if we are searching the same file
+ * o have a regular file with some data
+ * o can allocate the space we need...
+ *
+ * If we don't know about the current file, we leave the data
+ * from any previous file we may have loaded.
+ *
+ * DO *NOT* include this file in dependency output. The output may vary
+ * based on its contents, but since it is always optional input, it cannot
+ * be made to be required by make.
+ */
+ if (pzNewFile == NULL)
+ return NULL;
+
+ if ( (pzFile != NULL)
+ && (strcmp(pzFile, pzNewFile) == 0))
+ return pzText;
+
+ if ( (stat(pzNewFile, &sbuf) != 0)
+ || (! S_ISREG(sbuf.st_mode))
+ || (sbuf.st_size < 10) )
+ return NULL;
+
+ if (pzFile != NULL) {
+ AGFREE((void*)pzFile);
+ AGFREE((void*)pzText);
+ pzFile = pzText = NULL;
+ }
+
+ AGDUPSTR(pzFile, pzNewFile, "extract file");
+ pzIn = (char*)AGALOC(sbuf.st_size + 1, "Extract Text");
+
+ if (! HAVE_OPT(WRITABLE))
+ SET_OPT_WRITABLE;
+
+ pzText = (char const*)pzIn;
+
+ /*
+ * Suck up the file. We must read it all.
+ */
+ {
+ struct stat stbf;
+ FILE* fp = fopen(pzNewFile, "r");
+ if (fp == NULL)
+ goto bad_return;
+
+ if (fstat(fileno(fp), &stbf) != 0) {
+ fclose(fp);
+ goto bad_return;
+ }
+
+ if (outfile_time < stbf.st_mtime)
+ outfile_time = stbf.st_mtime;
+
+ do {
+ size_t sz = fread(pzIn, (size_t)1, (size_t)sbuf.st_size, fp);
+ if (sz == 0) {
+ fprintf(stderr, LD_EXTRACT_BAD_READ, errno, strerror(errno),
+ (int)sbuf.st_size, pzFile);
+ AG_ABEND(LD_EXTRACT_READ_FAIL);
+ }
+
+ pzIn += sz;
+ sbuf.st_size -= sz;
+ } while (sbuf.st_size > 0);
+
+ *pzIn = NUL;
+ fclose(fp);
+
+ if (dep_fp != NULL)
+ add_source_file(pzNewFile);
+ }
+
+ return pzText;
+
+ bad_return:
+
+ AGFREE((void*)pzFile);
+ pzFile = NULL;
+ AGFREE((void*)pzText);
+ pzText = NULL;
+
+ return pzText;
+}
+
+
+/**
+ * Could not find the file or could not find the markers.
+ * Either way, emit an empty enclosure.
+ */
+static SCM
+mk_empty_text(char const* pzStart, char const* pzEnd, SCM def)
+{
+ size_t mlen = strlen(pzStart) + strlen(pzEnd) + 3;
+ char* pzOut;
+
+ if (! AG_SCM_STRING_P(def)) {
+ pzOut = ag_scribble(mlen);
+ sprintf(pzOut, LINE_CONCAT3_FMT+3, pzStart, pzEnd);
+
+ } else {
+ char const * pzDef = ag_scm2zchars(def, "dft extr str");
+ mlen += AG_SCM_STRLEN(def) + 1;
+ pzOut = ag_scribble(mlen);
+ sprintf(pzOut, LINE_CONCAT3_FMT, pzStart, pzDef, pzEnd);
+ }
+
+ return AG_SCM_STR02SCM(pzOut);
+}
+
+
+/*
+ * If we got it, emit it.
+ */
+static SCM
+get_text(char const* pzText, char const* pzStart, char const* pzEnd, SCM def)
+{
+ char const* pzS = strstr(pzText, pzStart);
+ char const* pzE;
+
+ if (pzS == NULL)
+ return mk_empty_text(pzStart, pzEnd, def);
+
+ pzE = strstr(pzS, pzEnd);
+ if (pzE == NULL)
+ return mk_empty_text(pzStart, pzEnd, def);
+
+ pzE += strlen(pzEnd);
+
+ return AG_SCM_STR2SCM(pzS, (size_t)(pzE - pzS));
+}
+
+
+/*=gfunc extract
+ *
+ * what: extract text from another file
+ * general_use:
+ * exparg: file-name, name of file with text
+ * exparg: marker-fmt, format for marker text
+ * exparg: caveat, warn about changing marker, opt
+ * exparg: default, default initial text, opt
+ *
+ * doc:
+ *
+ * This function is used to help construct output files that may contain
+ * text that is carried from one version of the output to the next.
+ *
+ * The first two arguments are required, the second are optional:
+ *
+ * @itemize @bullet
+ * @item
+ * The @code{file-name} argument is used to name the file that
+ * contains the demarcated text.
+ * @item
+ * The @code{marker-fmt} is a formatting string that is used to construct
+ * the starting and ending demarcation strings. The sprintf function is
+ * given the @code{marker-fmt} with two arguments. The first is either
+ * "START" or "END". The second is either "DO NOT CHANGE THIS COMMENT"
+ * or the optional @code{caveat} argument.
+ * @item
+ * @code{caveat} is presumed to be absent if it is the empty string
+ * (@code{""}). If absent, ``DO NOT CHANGE THIS COMMENT'' is used
+ * as the second string argument to the @code{marker-fmt}.
+ * @item
+ * When a @code{default} argument is supplied and no pre-existing text
+ * is found, then this text will be inserted between the START and END
+ * markers.
+ * @end itemize
+ *
+ * @noindent
+ * The resulting strings are presumed to be unique within
+ * the subject file. As a simplified example:
+ *
+ * @example
+ * [+ (extract "fname" "// %s - SOMETHING - %s" ""
+ * "example default") +]
+ * @end example
+ * @noindent
+ * will result in the following text being inserted into the output:
+ *
+ * @example
+ * // START - SOMETHING - DO NOT CHANGE THIS COMMENT
+ * example default
+ * // END - SOMETHING - DO NOT CHANGE THIS COMMENT
+ * @end example
+ *
+ * @noindent
+ * The ``@code{example default}'' string can then be carried forward to
+ * the next generation of the output, @strong{@i{provided}} the output
+ * is not named "@code{fname}" @i{and} the old output is renamed to
+ * "@code{fname}" before AutoGen-eration begins.
+ *
+ * @table @strong
+ * @item NB:
+ * You can set aside previously generated source files inside the pseudo
+ * macro with a Guile/scheme function, extract the text you want to keep
+ * with this extract function. Just remember you should delete it at the
+ * end, too. Here is an example from my Finite State Machine generator:
+ *
+ * @example
+ * [+ AutoGen5 Template -*- Mode: text -*-
+ * h=%s-fsm.h c=%s-fsm.c
+ * (shellf
+ * "test -f %1$s-fsm.h && mv -f %1$s-fsm.h .fsm.head
+ * test -f %1$s-fsm.c && mv -f %1$s-fsm.c .fsm.code" (base-name))
+ * +]
+ * @end example
+ *
+ * This code will move the two previously produced output files to files
+ * named ".fsm.head" and ".fsm.code". At the end of the 'c' output
+ * processing, I delete them.
+ *
+ * @item also NB:
+ * This function presumes that the output file ought to be editable so
+ * that the code between the @code{START} and @code{END} marks can be edited
+ * by the template user. Consequently, when the @code{(extract ...)} function
+ * is invoked, if the @code{writable} option has not been specified, then
+ * it will be set at that point. If this is not the desired behavior, the
+ * @code{--not-writable} command line option will override this.
+ * Also, you may use the guile function @code{(chmod "file" mode-value)}
+ * to override whatever AutoGen is using for the result mode.
+ * @end table
+=*/
+SCM
+ag_scm_extract(SCM file, SCM marker, SCM caveat, SCM def)
+{
+ char const * pzStart;
+ char const * pzEnd;
+ char const * pzText;
+
+ if (! AG_SCM_STRING_P(file) || ! AG_SCM_STRING_P(marker))
+ return SCM_UNDEFINED;
+
+ pzText = load_extract_file(ag_scm2zchars(file, "extr file"));
+
+ {
+ char const * pzMarker = ag_scm2zchars(marker, "marker");
+ char const * pzCaveat = EXTRACT_CAVEAT;
+
+ if (AG_SCM_STRING_P(caveat) && (AG_SCM_STRLEN(caveat) > 0))
+ pzCaveat = ag_scm2zchars(caveat, "caveat");
+
+ pzStart = aprf(pzMarker, EXTRACT_START, pzCaveat);
+ pzEnd = aprf(pzMarker, EXTRACT_END, pzCaveat);
+ }
+
+ {
+ SCM res;
+
+ if (pzText == NULL)
+ res = mk_empty_text(pzStart, pzEnd, def);
+ else res = get_text(pzText, pzStart, pzEnd, def);
+
+ AGFREE((void*)pzStart);
+ AGFREE((void*)pzEnd);
+ return res;
+ }
+}
+
+
+/*=gfunc find_file
+ *
+ * what: locate a file in the search path
+ * exparg: file-name, name of file with text
+ * exparg: @suffix @ file suffix to try, too @ opt @
+ *
+ * doc:
+ *
+ * AutoGen has a search path that it uses to locate template and definition
+ * files. This function will search the same list for @file{file-name}, both
+ * with and without the @file{.suffix}, if provided.
+=*/
+SCM
+ag_scm_find_file(SCM file, SCM suffix)
+{
+ SCM res = SCM_UNDEFINED;
+
+ if (! AG_SCM_STRING_P(file))
+ scm_wrong_type_arg(FIND_FILE_NAME, 1, file);
+
+ {
+ char z[ AG_PATH_MAX+1 ];
+ char const * pz = ag_scm2zchars(file, "file-name");
+
+ /*
+ * The suffix is optional. If provided, it will be a string.
+ */
+ if (AG_SCM_STRING_P(suffix)) {
+ char* apz[2];
+ apz[0] = (char *)ag_scm2zchars(suffix, "file suffix");
+ apz[1] = NULL;
+ if (SUCCESSFUL(find_file(pz, z, (char const **)apz, NULL)))
+ res = AG_SCM_STR02SCM(z);
+
+ } else if (SUCCESSFUL(find_file(pz, z, NULL, NULL)))
+ res = AG_SCM_STR02SCM(z);
+ }
+
+ return res;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expExtract.c */
diff --git a/agen5/expFormat.c b/agen5/expFormat.c
new file mode 100644
index 0000000..1679e36
--- /dev/null
+++ b/agen5/expFormat.c
@@ -0,0 +1,874 @@
+
+/**
+ * @file expFormat.c
+ *
+ * Time-stamp: "2012-03-04 13:54:56 bkorb"
+ *
+ * This module implements formatting expression functions.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef enum {
+ LSEG_INFO = 1,
+ LSEG_DESC = 2,
+ LSEG_FULL = 3,
+ LSEG_NAME = 4
+} lic_segment_e_t;
+
+/*=gfunc dne
+ *
+ * what: '"Do Not Edit" warning'
+ *
+ * exparg: prefix, string for starting each output line
+ * exparg: first_prefix, for the first output line, opt
+ * exparg: optpfx, shifted prefix, opt
+ *
+ * doc:
+ * Generate a "DO NOT EDIT" or "EDIT WITH CARE" warning string.
+ * Which depends on whether or not the @code{--writable} command line
+ * option was set.
+ *
+ * The first argument may be an option: -d
+ *
+ * This will suppress the variable text (date and version information).
+ * If specified, then the "prefix" and "first" arguments are shifted
+ * to the next arguments.
+ *
+ * The first argument is a per-line string prefix. The optional second
+ * argument is a prefix for the first-line and, in read-only mode, activates
+ * the editor hints.
+ * @*
+ * @example
+ * -*- buffer-read-only: t -*- vi: set ro:
+ * @end example
+ * @noindent
+ * The warning string also includes information about the template used
+ * to construct the file and the definitions used in its instantiation.
+ *
+ * The optional third argument is used when the first argument is actually an
+ * invocation option and the prefix arguments get shifted. The first
+ * argument must be, specifically, "@code{-d}". That is used to signify that
+ * the date stamp should not be inserted into the output.
+=*/
+SCM
+ag_scm_dne(SCM prefix, SCM first, SCM opt)
+{
+ char const * date_str;
+ char const * pzRes;
+ char const * pzFirst;
+ size_t pfxLen;
+ char const * pzPrefix;
+ SCM res;
+
+ if (! AG_SCM_STRING_P(prefix))
+ return SCM_UNDEFINED;
+
+ date_str = NULL;
+ pzFirst = zNil;
+ pfxLen = AG_SCM_STRLEN(prefix);
+ pzPrefix = ag_scm2zchars(prefix, "dne-prefix");
+
+ /*
+ * Check for the ``-d'' option
+ */
+ if ((pfxLen == 2) && (strncmp(pzPrefix, "-d", (size_t)2) == 0)) {
+ date_str = zNil;
+ pfxLen = AG_SCM_STRLEN(first);
+ pzPrefix = ag_scm2zchars(first, "dne-prefix");
+ first = opt;
+ }
+
+ /*
+ * IF we also have a 'first' prefix string,
+ * THEN we set it to something other than ``zNil'' and deallocate later.
+ */
+ if (AG_SCM_STRING_P(first))
+ pzFirst = aprf(ENABLED_OPT(WRITABLE) ? "%s\n" : EXP_FMT_DNE1,
+ ag_scm2zchars(first, "first-prefix"), pzPrefix);
+
+ if (date_str == NULL) {
+ static char const tim_fmt[] =
+ " %B %e, %Y at %r by AutoGen " AUTOGEN_VERSION;
+
+ size_t const tsiz = sizeof(tim_fmt) + sizeof("september") * 2;
+ time_t curTime = time(NULL);
+ struct tm* pTime = localtime(&curTime);
+
+ date_str = ag_scribble(tsiz);
+ strftime((char *)date_str, tsiz, tim_fmt, pTime);
+ }
+
+ {
+ char const * pz;
+ out_stack_t * pfp = cur_fpstack;
+ char const * tpl_name = strrchr(tpl_fname, DIRCH);
+ if (tpl_name == NULL)
+ tpl_name = tpl_fname;
+ else
+ tpl_name++;
+
+ while (pfp->stk_flags & FPF_UNLINK) pfp = pfp->stk_prev;
+ if (! ENABLED_OPT(DEFINITIONS))
+ pz = "<<no definitions>>";
+
+ else if (*oops_pfx != NUL)
+ pz = "<<CGI-definitions>>";
+
+ else {
+ pz = OPT_ARG(DEFINITIONS);
+ if (strcmp(pz, "-") == 0)
+ pz = "stdin";
+ }
+
+ pzRes = aprf(ENABLED_OPT(WRITABLE) ? EXP_FMT_DNE2 : EXP_FMT_DNE,
+ pzPrefix, pfp->stk_fname, date_str,
+ pz, tpl_name, pzFirst);
+ }
+
+ if (pzRes == NULL)
+ AG_ABEND("Allocating Do-Not-Edit string");
+
+ res = AG_SCM_STR02SCM(pzRes);
+
+ /*
+ * Deallocate any temporary buffers. pzFirst either points to
+ * the zNil string, or to an allocated buffer.
+ */
+ AGFREE((void*)pzRes);
+ if (pzFirst != zNil)
+ AGFREE((void*)pzFirst);
+
+ return res;
+}
+
+
+/*=gfunc error
+ *
+ * what: display message and exit
+ *
+ * exparg: @message@message to display before exiting@@
+ * doc:
+ *
+ * The argument is a string that printed out as part of an error
+ * message. The message is formed from the formatting string:
+ *
+ * @example
+ * DEFINITIONS ERROR in %s line %d for %s: %s\n
+ * @end example
+ *
+ * The first three arguments to this format are provided by the
+ * routine and are: The name of the template file, the line within
+ * the template where the error was found, and the current output
+ * file name.
+ *
+ * After displaying the message, the current output file is removed
+ * and autogen exits with the EXIT_FAILURE error code. IF, however,
+ * the argument begins with the number 0 (zero), or the string is the
+ * empty string, then processing continues with the next suffix.
+=*/
+SCM
+ag_scm_error(SCM res)
+{
+ char const * pzMsg;
+ tSuccess abrt = FAILURE;
+ char zNum[16];
+ int msgLen;
+
+ switch (ag_scm_type_e(res)) {
+ case GH_TYPE_BOOLEAN:
+ if (AG_SCM_FALSEP(res))
+ abrt = PROBLEM;
+ pzMsg = zNil;
+ break;
+
+ case GH_TYPE_NUMBER:
+ {
+ unsigned long val = AG_SCM_TO_ULONG(res);
+ if (val == 0)
+ abrt = PROBLEM;
+ snprintf(zNum, sizeof(zNum), "%d", (int)val);
+ pzMsg = zNum;
+ break;
+ }
+
+ case GH_TYPE_CHAR:
+ zNum[0] = AG_SCM_CHAR(res);
+ if ((zNum[0] == NUL) || (zNum[0] == '0'))
+ abrt = PROBLEM;
+ zNum[1] = NUL;
+ pzMsg = zNum;
+ break;
+
+ case GH_TYPE_STRING:
+ pzMsg = ag_scm2zchars(res, "error string");
+ pzMsg = SPN_WHITESPACE_CHARS(pzMsg);
+ msgLen = strlen(pzMsg);
+
+ /*
+ * IF the message starts with the number zero,
+ * OR the message is the empty string,
+ * THEN this is just a warning that is ignored
+ */
+ if (msgLen <= 0)
+ abrt = PROBLEM;
+ else if (IS_DEC_DIGIT_CHAR(*pzMsg) && (strtol(pzMsg, NULL, 0) == 0))
+ abrt = PROBLEM;
+ break;
+
+ default:
+ pzMsg = BAD_MSG_STR;
+ }
+
+ /*
+ * IF there is a message,
+ * THEN print it.
+ */
+ if (*pzMsg != NUL) {
+ char* pz = aprf(DEF_NOTE_FMT, (abrt != PROBLEM) ? ERROR_STR : WARN_STR,
+ current_tpl->td_file, cur_macro->md_line,
+ cur_fpstack->stk_fname, pzMsg);
+ if (abrt != PROBLEM)
+ AG_ABEND(pz);
+ fputs(pz, trace_fp);
+ AGFREE((void*)pz);
+ }
+
+ longjmp(abort_jmp_buf, abrt);
+ /* NOTREACHED */
+ return SCM_UNDEFINED;
+}
+
+/**
+ * Assemble the copyright preamble and long license description.
+ *
+ * @param txt a pointer to the first of two newlines separating
+ * copyright information from the description.
+ */
+static void
+assemble_full_desc(char * txt, char const * pfx)
+{
+ char * pd;
+ char * md;
+
+ size_t prefix_len = strlen(pfx) + 1;
+ while ( (prefix_len > 0)
+ && IS_WHITESPACE_CHAR(pfx[prefix_len - 2]))
+ prefix_len--;
+
+ /*
+ * Preserve the first newline. Set the move destination
+ * out past where we will be inserting the "<PFX>\n" marker.
+ */
+ pd = txt + 1; /* prefix destination */
+ md = pd + prefix_len; /* move destination */
+
+ while (*txt == NL) txt++;
+ /*
+ * Maybe there were exactly enough NL characters we don't need to move
+ */
+ if (md != txt)
+ memmove(md, txt, strlen(txt) + 1);
+ memmove(pd, pfx, --prefix_len);
+ pd[prefix_len] = NL;
+
+ /*
+ * Look for a trailing license name and trim it and trailing white space
+ */
+ txt = strstr(md, "\n\n");
+ if (txt == NULL)
+ txt = md + strlen(md);
+
+ while ( (txt > md)
+ && IS_WHITESPACE_CHAR(txt[-1])) txt--;
+ *txt = NUL;
+}
+
+/**
+ * Trim off the license name. It is the third double-newline stanza
+ * in the license file.
+ *
+ * @param p a pointer to the first of two newlines separating
+ * copyright information from the description.
+ * @return pointer to second stanza, sans the license name trailer.
+ */
+static char *
+trim_lic_name(char * p)
+{
+ char * res;
+ /* skip the leading white space. It starts with NL. */
+ p = SPN_WHITESPACE_CHARS(p + 1);
+ if (*p == NUL)
+ return p;
+
+ res = p;
+
+ /*
+ * The last section ends with two consecutive new lines.
+ * All trailing newlines are trimmed (not all white space).
+ */
+ p = strstr(p, "\n\n");
+ if (p == NULL)
+ p = res + strlen(res);
+ while ( (p > res)
+ && IS_WHITESPACE_CHAR(p[-1])) p--;
+ *p = NUL;
+
+ return res;
+}
+
+/**
+ * Extract the license name. It is the third double-newline stanza
+ * in the license file.
+ *
+ * @param txt a pointer to the first of two newlines separating
+ * copyright information from the description.
+ * @return pointer to the license name trailer.
+ */
+static char *
+get_lic_name(char * p)
+{
+ char * scan = p;
+ while (*(++scan) == NL) ; /* skip the leading NL's. */
+
+ /*
+ * Find the third stanza. If there. If not, we supply some static
+ * text: "an unknown license"
+ */
+ scan = strstr(scan, "\n\n");
+ if (scan == NULL) {
+ strcpy(p, EXP_FMT_BAD_LIC);
+ return p;
+ }
+ while (*scan == NL) scan++;
+ return scan;
+}
+
+/**
+ * Find the kind of text being requested. It may be "full" (the first
+ * two stanzas), "info" (the first -- copyright info + license name),
+ * "description" (the second -- a one paragraph description), or
+ * "name" -- the third stanza.
+ *
+ * @param txt a pointer to the first of two newlines separating
+ * copyright information from the description.
+ * @return pointer to the requested text.
+ */
+static char *
+find_lic_text(
+ lic_segment_e_t segment, SCM lic, ssize_t * txt_len, char const * pfx)
+{
+ static char const * const lic_sfx[] = { FIND_LIC_TEXT_SFX, NULL };
+
+ char const * lic_pz = ag_scm2zchars(lic, "license");
+ char fname[ AG_PATH_MAX ];
+ char * ftext;
+ size_t flen;
+
+ /*
+ * auto-convert "bsd" into "mbsd" for compatibility.
+ */
+ if (strcmp(lic_pz, FIND_LIC_TEXT_MBSD+1) == 0)
+ lic_pz = FIND_LIC_TEXT_MBSD;
+
+ if (! SUCCESSFUL(find_file(lic_pz, fname, lic_sfx, NULL)))
+ return NULL;
+
+ {
+ struct stat stbf;
+ if (stat(fname, &stbf) != 0)
+ AG_CANT(FIND_LIC_TEXT_NO_LIC, fname);
+ if (! S_ISREG(stbf.st_mode)) {
+ errno = EINVAL;
+ AG_CANT(FIND_LIC_TEXT_BAD_FILE, fname);
+ }
+ flen = stbf.st_size;
+ }
+
+ ftext = ag_scribble(flen + EXP_FMT_BAD_LIC_LEN + 1);
+ *txt_len = flen;
+
+ {
+ FILE * fp = fopen(fname, "r");
+
+ if (fp == NULL)
+ AG_CANT(FIND_LIC_TEXT_OPEN, fname);
+
+ if (fread(ftext, 1, flen, fp) != flen)
+ AG_CANT(FIND_LIC_TEXT_BAD_FILE, fname);
+
+ ftext[flen] = NUL;
+ fclose(fp);
+ }
+
+ if (dep_fp != NULL)
+ add_source_file(fname);
+
+ {
+ char * p = strstr(ftext, DOUBLE_NEWLINE);
+
+ if (p == NULL)
+ AG_ABEND(aprf(FIND_LIC_TEXT_INVAL, fname));
+
+ switch (segment) {
+ case LSEG_INFO: p[1] = NUL; break;
+ case LSEG_DESC: ftext = trim_lic_name(p); break;
+ case LSEG_NAME: ftext = get_lic_name(p); break;
+ case LSEG_FULL: assemble_full_desc(p, pfx); break;
+ }
+ }
+
+ return ftext;
+}
+
+/**
+ * Construct an SCM for the kind of text being requested.
+ *
+ * It may be "full" (the first two stanzas), "info" (the first -- copyright
+ * info + license name), "description" (the second -- a one paragraph
+ * description), or "name" -- the third stanza.
+ *
+ * @param seg which segment of license is desired
+ * @param lic The name of the license
+ * @param prog the name of the program
+ * @param pfx a per-line prefix
+ * @param owner who owns the copyright
+ * @param years the copyright years
+ *
+ * @return the SCM-ized string
+ */
+static SCM
+construct_license(
+ lic_segment_e_t seg, SCM lic, SCM prog, SCM pfx, SCM owner, SCM years)
+{
+ static SCM subs = SCM_UNDEFINED;
+ static SCM empty = SCM_UNDEFINED;
+
+ SCM vals = SCM_UNDEFINED;
+ char * lic_text;
+ ssize_t text_len;
+ char const * pfx_pz = ag_scm2zchars(pfx, "lic-prefix");
+
+ if (subs == SCM_UNDEFINED) {
+ static char const * const slst[] = {
+ MK_LIC_PROG, MK_LIC_PFX, MK_LIC_OWN, MK_LIC_YRS
+ };
+ subs = scm_gc_protect_object(
+ scm_list_4(AG_SCM_STR02SCM(slst[0]), AG_SCM_STR02SCM(slst[1]),
+ AG_SCM_STR02SCM(slst[2]), AG_SCM_STR02SCM(slst[3])));
+
+ empty = scm_gc_protect_object(AG_SCM_STR02SCM(""));
+ }
+
+ if (! AG_SCM_STRING_P(lic))
+ AG_ABEND(MK_LIC_NOT_STR);
+
+ lic_text = find_lic_text(seg, lic, &text_len, pfx_pz);
+ if (lic_text == NULL)
+ AG_ABEND(aprf(MK_LIC_NO_LIC, ag_scm2zchars(lic, "lic")));
+
+ if (! AG_SCM_STRING_P(owner)) owner = empty;
+ if (! AG_SCM_STRING_P(years)) years = empty;
+ vals = scm_list_4(prog, pfx, owner, years);
+
+ do_multi_subs(&lic_text, &text_len, subs, vals);
+
+ return AG_SCM_STR02SCM(lic_text);
+}
+
+/*=gfunc license_full
+ *
+ * what: Emit the licensing information and description
+ * general_use:
+ *
+ * exparg: license, name of license type
+ * exparg: prog-name, name of the program under the GPL
+ * exparg: prefix, String for starting each output line
+ * exparg: owner, owner of the program, optional
+ * exparg: years, copyright years, optional
+ *
+ * doc:
+ *
+ * Emit all the text that @code{license-info} and @code{license-description}
+ * would emit (@pxref{SCM license-info, @code{license-info}},
+ * and @pxref{SCM license-description, @code{license-description}}),
+ * with all the same substitutions.
+ *
+ * All of these depend upon the existence of a license file named after the
+ * @code{license} argument with a @code{.lic} suffix. That file should
+ * contain three blocks of text, each separated by two or more newline
+ * characters.
+ *
+ * The first section describes copyright attribution and the name of the usage
+ * licence. For GNU software, this should be the text that is to be displayed
+ * with the program version. Four text markers can be replaced: <PFX>,
+ * <program>, <years> and <owner>.
+ *
+ * The second section is a short description of the terms of the license.
+ * This is typically the kind of text that gets displayed in the header of
+ * source files. The third section is strictly the name of the license
+ * without any substitution markers. Only the <PFX>, <owner> and <program>
+ * markers are substituted.
+ *
+ * The third section is strictly the name of the license.
+ * No marker substitutions are performed.
+ *
+ * @example
+ * <PFX>Copyright (C) <years> <owner>, all rights reserved.
+ * <PFX>This is free software. It is licensed for use,
+ * <PFX>modification and redistribution under the terms
+ * <PFX>of the GNU General Public License, version 3 or later
+ * <PFX> <http://gnu.org/licenses/gpl.html>
+ *
+ * <PFX><program> is free software: you can redistribute it
+ * <PFX>and/or modify it under the terms of the GNU General
+ * <PFX>Public License as published by the Free Software ...
+ *
+ * the GNU General Public License, version 3 or later
+ * @end example
+=*/
+SCM
+ag_scm_license_full(SCM lic, SCM prog, SCM pfx, SCM owner, SCM years)
+{
+ return construct_license(LSEG_FULL, lic, prog, pfx, owner, years);
+}
+
+/*=gfunc license_description
+ *
+ * what: Emit a license description
+ * general_use:
+ *
+ * exparg: license, name of license type
+ * exparg: prog-name, name of the program under the GPL
+ * exparg: prefix, String for starting each output line
+ * exparg: owner, owner of the program, optional
+ *
+ * doc:
+ *
+ * Emit a string that contains a detailed license description, with
+ * substitutions for program name, copyright holder and a per-line prefix.
+ * This is the text typically used as part of a source file header.
+ * For more details, @xref{SCM license-full, the license-full command}.
+ *
+=*/
+SCM
+ag_scm_license_description(SCM lic, SCM prog, SCM pfx, SCM owner)
+{
+ return construct_license(LSEG_DESC, lic, prog, pfx, owner, SCM_UNDEFINED);
+}
+
+/*=gfunc license_info
+ *
+ * what: Emit the licensing information and copyright years
+ * general_use:
+ *
+ * exparg: license, name of license type
+ * exparg: prog-name, name of the program under the GPL
+ * exparg: prefix, String for starting each output line
+ * exparg: owner, owner of the program, optional
+ * exparg: years, copyright years, optional
+ *
+ * doc:
+ *
+ * Emit a string that contains the licensing description, with some
+ * substitutions for program name, copyright holder, a list of years when the
+ * source was modified, and a per-line prefix. This text typically includes a
+ * brief license description and is often printed out when a program starts
+ * running or as part of the @code{--version} output.
+ * For more details, @xref{SCM license-full, the license-full command}.
+ *
+=*/
+SCM
+ag_scm_license_info(SCM lic, SCM prog, SCM pfx, SCM owner, SCM years)
+{
+ return construct_license(LSEG_INFO, lic, prog, pfx, owner, years);
+}
+
+/*=gfunc license_name
+ *
+ * what: Emit the name of the license
+ * general_use:
+ *
+ * exparg: license, name of license type
+ *
+ * doc:
+ *
+ * Emit a string that contains the full name of the license.
+=*/
+SCM
+ag_scm_license_name(SCM lic)
+{
+ ssize_t text_len;
+ char * txt = find_lic_text(LSEG_NAME, lic, &text_len, "");
+ char * e;
+
+ txt = SPN_WHITESPACE_CHARS(txt);
+ e = SPN_WHITESPACE_BACK(txt, txt);
+ *e = NUL;
+ lic = AG_SCM_STR02SCM(txt);
+ return lic;
+}
+
+/*=gfunc gpl
+ *
+ * what: GNU General Public License
+ * general_use:
+ *
+ * exparg: prog-name, name of the program under the GPL
+ * exparg: prefix, String for starting each output line
+ *
+ * doc:
+ *
+ * Emit a string that contains the GNU General Public License.
+ * This function is now deprecated. Please @xref{SCM license-description}.
+=*/
+SCM
+ag_scm_gpl(SCM prog_name, SCM prefix)
+{
+ static SCM lic = SCM_UNDEFINED;
+
+ if (lic == SCM_UNDEFINED)
+ lic = scm_gc_protect_object(AG_SCM_STR02SCM(FIND_LIC_TEXT_LGPL+1));
+ return ag_scm_license_description(lic, prog_name, prefix, SCM_UNDEFINED);
+}
+
+/*=gfunc agpl
+ *
+ * what: GNU Affero General Public License
+ * general_use:
+ *
+ * exparg: prog-name, name of the program under the GPL
+ * exparg: prefix, String for starting each output line
+ *
+ * doc:
+ *
+ * Emit a string that contains the GNU Affero General Public License.
+ * This function is now deprecated. Please @xref{SCM license-description}.
+=*/
+SCM
+ag_scm_agpl(SCM prog_name, SCM prefix)
+{
+ static SCM lic = SCM_UNDEFINED;
+
+ if (lic == SCM_UNDEFINED)
+ lic = scm_gc_protect_object(AG_SCM_STR02SCM(FIND_LIC_TEXT_AGPL));
+ return ag_scm_license_description(lic, prog_name, prefix, SCM_UNDEFINED);
+}
+
+/*=gfunc lgpl
+ *
+ * what: GNU Library General Public License
+ * general_use:
+ *
+ * exparg: prog_name, name of the program under the LGPL
+ * exparg: owner, Grantor of the LGPL
+ * exparg: prefix, String for starting each output line
+ *
+ * doc:
+ *
+ * Emit a string that contains the GNU Library General Public License.
+ * This function is now deprecated. Please @xref{SCM license-description}.
+=*/
+SCM
+ag_scm_lgpl(SCM prog_name, SCM owner, SCM prefix)
+{
+ static SCM lic = SCM_UNDEFINED;
+
+ if (lic == SCM_UNDEFINED)
+ lic = scm_gc_protect_object(AG_SCM_STR02SCM(FIND_LIC_TEXT_LGPL));
+ return ag_scm_license_description(lic, prog_name, prefix, owner);
+}
+
+/*=gfunc bsd
+ *
+ * what: BSD Public License
+ * general_use:
+ *
+ * exparg: prog_name, name of the program under the BSD
+ * exparg: owner, Grantor of the BSD License
+ * exparg: prefix, String for starting each output line
+ *
+ * doc:
+ *
+ * Emit a string that contains the Free BSD Public License.
+ * This function is now deprecated. Please @xref{SCM license-description}.
+ *
+=*/
+SCM
+ag_scm_bsd(SCM prog_name, SCM owner, SCM prefix)
+{
+ static SCM lic = SCM_UNDEFINED;
+
+ if (lic == SCM_UNDEFINED)
+ lic = scm_gc_protect_object(AG_SCM_STR02SCM(FIND_LIC_TEXT_MBSD));
+ return ag_scm_license_description(lic, prog_name, prefix, owner);
+}
+
+/*=gfunc license
+ *
+ * what: an arbitrary license
+ * general_use:
+ *
+ * exparg: lic_name, file name of the license
+ * exparg: prog_name, name of the licensed program or library
+ * exparg: owner, Grantor of the License
+ * exparg: prefix, String for starting each output line
+ *
+ * doc:
+ * Emit a string that contains the named license.
+ * This function is now deprecated. Please @xref{SCM license-description}.
+=*/
+SCM
+ag_scm_license(SCM license, SCM prog_name, SCM owner, SCM prefix)
+{
+ char const * pzPfx = ag_scm2zchars(prefix, "GPL line prefix");
+ char const * pzPrg = ag_scm2zchars(prog_name, "program name");
+ char const * pzOwner = ag_scm2zchars(owner, "owner");
+ static struct {
+ char const * pzFN;
+ tmap_info_t mi;
+ } lic = { NULL, { NULL, 0, 0, 0, 0, 0, 0, 0 }};
+
+ char* pzRes;
+
+ if (! AG_SCM_STRING_P(license))
+ return SCM_UNDEFINED;
+
+ {
+ static char const * const apzSfx[] = { MK_LIC_SFX, NULL };
+ static char fname[ AG_PATH_MAX ];
+ char const * pzLicense = ag_scm2zchars(license, "lic file");
+
+ /*
+ * Find the template file somewhere
+ */
+ if (! SUCCESSFUL(find_file(pzLicense, fname, apzSfx, NULL))) {
+ errno = ENOENT;
+ AG_CANT(MK_LIC_NO_LIC, pzLicense);
+ }
+
+ if ((lic.pzFN != NULL) && (strcmp(fname, lic.pzFN) != 0)) {
+ text_munmap(&lic.mi);
+ AGFREE((void*)lic.pzFN);
+ lic.pzFN = NULL;
+ }
+
+ if (lic.pzFN == NULL) {
+ text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &lic.mi);
+ if (TEXT_MMAP_FAILED_ADDR(lic.mi.txt_data))
+ AG_ABEND(aprf(MK_LIC_NO_OPEN, pzLicense));
+
+ if (dep_fp != NULL)
+ add_source_file(pzLicense);
+
+ AGDUPSTR(lic.pzFN, fname, "lic f name");
+ }
+ }
+
+ /*
+ * Trim trailing white space.
+ */
+ {
+ char* pz = (char*)lic.mi.txt_data + lic.mi.txt_size;
+ while ( (pz > (char*)lic.mi.txt_data)
+ && IS_WHITESPACE_CHAR(pz[-1]))
+ pz--;
+ *pz = NUL;
+ }
+
+ /*
+ * Get the addresses of the program name prefix and owner strings.
+ * Make sure they are reasonably sized (less than
+ * SCRIBBLE_SIZE). Copy them to the scratch buffer.
+ */
+ if (AG_SCM_STRLEN(prog_name) >= SCRIBBLE_SIZE)
+ AG_ABEND(aprf(MK_LIC_TOO_BIG_FMT, MK_LIC_BIG_PROG, SCRIBBLE_SIZE));
+
+ if (AG_SCM_STRLEN(prefix) >= SCRIBBLE_SIZE)
+ AG_ABEND(aprf(MK_LIC_TOO_BIG_FMT, MK_LIC_BIG_PFX, SCRIBBLE_SIZE));
+
+ if (AG_SCM_STRLEN(owner) >= SCRIBBLE_SIZE)
+ AG_ABEND(aprf(MK_LIC_TOO_BIG_FMT, MK_LIC_BIG_OWN, SCRIBBLE_SIZE));
+
+ /*
+ * Reformat the string with the given arguments
+ */
+ pzRes = aprf((char*)lic.mi.txt_data, pzPrg, pzOwner);
+ {
+ int pfx_size = strlen(pzPfx);
+ char* pzScan = pzRes;
+ char* pzOut;
+ char* pzSaveRes;
+ size_t out_size = pfx_size;
+
+ /*
+ * Figure out how much space we need (text size plus
+ * a prefix size for each newline)
+ */
+ for (;;) {
+ switch (*(pzScan++)) {
+ case NUL:
+ goto exit_count;
+ case NL:
+ out_size += pfx_size;
+ /* FALLTHROUGH */
+ default:
+ out_size++;
+ }
+ } exit_count:;
+
+ /*
+ * Create our output buffer and insert the first prefix
+ */
+ pzOut = pzSaveRes = ag_scribble(out_size);
+
+ strcpy(pzOut, pzPfx);
+ pzOut += pfx_size;
+ pzScan = pzRes;
+
+ for (;;) {
+ switch (*(pzOut++) = *(pzScan++)) {
+ case NUL:
+ goto exit_copy;
+
+ case NL:
+ strcpy(pzOut, pzPfx);
+ pzOut += pfx_size;
+ break;
+
+ default:
+ break;
+ }
+ }
+ exit_copy:;
+
+ /*
+ * We allocated a temporary buffer that has all the
+ * formatting done, but need the prefixes on each line.
+ */
+ AGFREE((void*)pzRes);
+
+ return AG_SCM_STR2SCM(pzSaveRes, (size_t)((pzOut - pzSaveRes) - 1));
+ }
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expFormat.c */
diff --git a/agen5/expGperf.c b/agen5/expGperf.c
new file mode 100644
index 0000000..902f18b
--- /dev/null
+++ b/agen5/expGperf.c
@@ -0,0 +1,156 @@
+/**
+ * @file expGperf.c
+ *
+ * Time-stamp: "2012-01-15 08:38:23 bkorb"
+ *
+ * Create a perfect hash function program and use it to compute
+ * index values for a list of provided names. It also documents how
+ * to incorporate that hashing function into a generated C program.
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SHELL_ENABLED
+HIDE_FN(SCM ag_scm_make_gperf(SCM name, SCM hlist))
+{
+ return SCM_UNDEFINED;
+}
+
+HIDE_FN(SCM ag_scm_gperf(SCM name, SCM str))
+{
+ return SCM_UNDEFINED;
+}
+#else
+
+/*=gfunc make_gperf
+ *
+ * what: build a perfect hash function program
+ * general_use:
+ *
+ * exparg: name , name of hash list
+ * exparg: strings , list of strings to hash ,, list
+ *
+ * doc: Build a program to perform perfect hashes of a known list of input
+ * strings. This function produces no output, but prepares a program
+ * named, @file{gperf_<name>} for use by the gperf function
+ * @xref{SCM gperf}.
+ *
+ * This program will be obliterated as AutoGen exits.
+ * However, you may incorporate the generated hashing function
+ * into your C program with commands something like the following:
+ *
+ * @example
+ * [+ (shellf "sed '/^int main(/,$d;/^#line/d' $@{gpdir@}/%s.c"
+ * name ) +]
+ * @end example
+ *
+ * where @code{name} matches the name provided to this @code{make-perf}
+ * function. @code{gpdir} is the variable used to store the name of the
+ * temporary directory used to stash all the files.
+=*/
+SCM
+ag_scm_make_gperf(SCM name, SCM hlist)
+{
+ static bool do_cleanup = true;
+
+ char const * pzName = ag_scm2zchars(name, "gperf name");
+ char const * pzList;
+ SCM newline = AG_SCM_STR2SCM(NEWLINE, (size_t)1);
+
+ if (! AG_SCM_STRING_P(name))
+ return SCM_UNDEFINED;
+
+ /*
+ * Construct the newline separated list of values
+ */
+ hlist = ag_scm_join(newline, hlist);
+ pzList = ag_scm2zchars(hlist, "hash list");
+
+ /*
+ * Stash the concatenated list somewhere, hopefully without an alloc.
+ */
+ {
+ char * cmd = aprf(MK_GPERF_SCRIPT, zMakeProg, pzName, pzList);
+
+ /*
+ * Run the command and ignore the results.
+ * In theory, the program should be ready.
+ */
+ pzList = shell_cmd(cmd);
+ AGFREE(cmd);
+
+ if (pzList != NULL)
+ free((void *)pzList);
+ }
+
+ if (do_cleanup) {
+ SCM_EVAL_CONST(MAKE_GPERF_CLEANUP);
+ do_cleanup = false;
+ }
+
+ return SCM_BOOL_T;
+}
+
+
+/*=gfunc gperf
+ *
+ * what: perform a perfect hash function
+ * general_use:
+ *
+ * exparg: name , name of hash list
+ * exparg: str , string to hash
+ *
+ * doc: Perform the perfect hash on the input string. This is only useful if
+ * you have previously created a gperf program with the @code{make-gperf}
+ * function @xref{SCM make-gperf}. The @code{name} you supply here must
+ * match the name used to create the program and the string to hash must
+ * be one of the strings supplied in the @code{make-gperf} string list.
+ * The result will be a perfect hash index.
+ *
+ * See the documentation for @command{gperf(1GNU)} for more details.
+=*/
+SCM
+ag_scm_gperf(SCM name, SCM str)
+{
+ char const * cmd;
+ char const * key2hash = ag_scm2zchars(str, "key-to-hash");
+ char const * gp_name = ag_scm2zchars(name, "gperf name");
+
+ /*
+ * Format the gperf command and check the result. If it fits in
+ * scribble space, use that.
+ * (If it does fit, then the test string fits already).
+ */
+ cmd = aprf(RUN_GPERF_CMD, gp_name, key2hash);
+ key2hash = shell_cmd(cmd);
+ if (*key2hash == NUL)
+ str = SCM_UNDEFINED;
+ else
+ str = AG_SCM_STR02SCM(key2hash);
+
+ AGFREE((void *)cmd);
+ AGFREE((void *)key2hash);
+ return str;
+}
+#endif
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expGperf.c */
diff --git a/agen5/expGuile.c b/agen5/expGuile.c
new file mode 100644
index 0000000..e05a2a7
--- /dev/null
+++ b/agen5/expGuile.c
@@ -0,0 +1,522 @@
+
+/**
+ * @file expGuile.c
+ *
+ * Time-stamp: "2012-03-04 19:06:37 bkorb"
+ *
+ * This module implements the expression functions that should
+ * be part of Guile.
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+LOCAL teGuileType
+ag_scm_type_e(SCM typ)
+{
+ if (AG_SCM_BOOL_P( typ)) return GH_TYPE_BOOLEAN;
+ if (AG_SCM_SYM_P( typ)) return GH_TYPE_SYMBOL;
+ if (AG_SCM_STRING_P(typ)) return GH_TYPE_STRING;
+ if (AG_SCM_IS_PROC( typ)) return GH_TYPE_PROCEDURE;
+ if (AG_SCM_CHAR_P( typ)) return GH_TYPE_CHAR;
+ if (AG_SCM_VEC_P( typ)) return GH_TYPE_VECTOR;
+ if (AG_SCM_PAIR_P( typ)) return GH_TYPE_PAIR;
+ if (AG_SCM_NUM_P( typ)) return GH_TYPE_NUMBER;
+ if (AG_SCM_LIST_P( typ)) return GH_TYPE_LIST;
+
+ return GH_TYPE_UNDEFINED;
+}
+
+
+LOCAL SCM
+ag_scm_c_eval_string_from_file_line(
+ char const * pzExpr, char const * pzFile, int line)
+{
+ SCM port = scm_open_input_string(AG_SCM_STR02SCM(pzExpr));
+
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING)
+ fprintf(trace_fp, TRACE_EVAL_STRING, pzFile, line, pzExpr);
+
+ {
+ static SCM file = SCM_UNDEFINED;
+ static char * pzOldFile = NULL;
+
+ if ((pzOldFile == NULL) || (strcmp(pzOldFile, pzFile) != 0)) {
+ if (pzOldFile != NULL)
+ AGFREE(pzOldFile);
+ AGDUPSTR(pzOldFile, pzFile, "scheme source");
+ file = AG_SCM_STR02SCM(pzFile);
+ }
+
+ {
+ SCM ln = AG_SCM_INT2SCM(line);
+ scm_set_port_filename_x(port, file);
+ scm_set_port_line_x(port, ln);
+ scm_set_port_column_x(port, SCM_INUM0);
+ }
+ }
+
+ {
+ SCM ans = SCM_UNSPECIFIED;
+
+ /* Read expressions from that port; ignore the values. */
+ for (;;) {
+ SCM form = scm_read(port);
+ if (SCM_EOF_OBJECT_P(form))
+ break;
+ ans = scm_primitive_eval_x(form);
+ }
+
+ return ans;
+ }
+}
+
+
+/*=gfunc max
+ *
+ * what: maximum value in list
+ * general_use:
+ *
+ * exparg: list , list of values. Strings are converted to numbers ,, list
+ *
+ * doc: Return the maximum value in the list
+=*/
+SCM
+ag_scm_max(SCM list)
+{
+ int len;
+ SCM car;
+ long max_val = LONG_MIN;
+# ifndef MAX
+# define MAX(m,v) ((v > m) ? v : m)
+# endif
+ len = scm_ilength(list);
+ if (len <= 0)
+ return SCM_UNDEFINED;
+
+ while (--len >= 0) {
+ long val;
+
+ car = SCM_CAR(list);
+ list = SCM_CDR(list);
+
+ switch (ag_scm_type_e(car)) {
+ case GH_TYPE_BOOLEAN:
+ if (car == SCM_BOOL_F) {
+ val = 0;
+ } else {
+ val = 1;
+ }
+ break;
+
+ case GH_TYPE_CHAR:
+ val = (int)AG_SCM_CHAR(car);
+ break;
+
+ case GH_TYPE_NUMBER:
+ val = AG_SCM_TO_LONG(car);
+ break;
+
+ case GH_TYPE_STRING:
+ val = strtol(ag_scm2zchars(car, "number-in-string"), NULL, 0);
+ break;
+
+ default:
+ continue;
+ }
+ max_val = MAX(max_val, val);
+ }
+
+ return AG_SCM_FROM_LONG(max_val);
+}
+
+
+/*=gfunc min
+ *
+ * what: minimum value in list
+ * general_use:
+ *
+ * exparg: list , list of values. Strings are converted to numbers ,, list
+ *
+ * doc: Return the minimum value in the list
+=*/
+SCM
+ag_scm_min(SCM list)
+{
+ int len;
+ SCM car;
+ long min_val = LONG_MAX;
+# ifndef MIN
+# define MIN(m,v) ((v < m) ? v : m)
+# endif
+ len = scm_ilength(list);
+ if (len <= 0)
+ return SCM_UNDEFINED;
+
+ while (--len >= 0) {
+ long val;
+
+ car = SCM_CAR(list);
+ list = SCM_CDR(list);
+
+ switch (ag_scm_type_e(car)) {
+ case GH_TYPE_BOOLEAN:
+ if (car == SCM_BOOL_F) {
+ val = 0;
+ } else {
+ val = 1;
+ }
+ break;
+
+ case GH_TYPE_CHAR:
+ val = (int)AG_SCM_CHAR(car);
+ break;
+
+ case GH_TYPE_NUMBER:
+ val = AG_SCM_TO_LONG(car);
+ break;
+
+ case GH_TYPE_STRING:
+ val = strtol(ag_scm2zchars(car, "number-in-string"), NULL, 0);
+ break;
+
+ default:
+ continue;
+ }
+ min_val = MIN(min_val, val);
+ }
+
+ return AG_SCM_FROM_LONG(min_val);
+}
+
+
+/*=gfunc sum
+ *
+ * what: sum of values in list
+ * general_use:
+ *
+ * exparg: list , list of values. Strings are converted to numbers ,, list
+ *
+ * doc: Compute the sum of the list of expressions.
+=*/
+SCM
+ag_scm_sum(SCM list)
+{
+ int len = scm_ilength(list);
+ long sum = 0;
+
+ if (len <= 0)
+ return AG_SCM_INT2SCM(0);
+
+ do {
+ SCM car = SCM_CAR(list);
+ list = SCM_CDR(list);
+ switch (ag_scm_type_e(car)) {
+ default:
+ return SCM_UNDEFINED;
+
+ case GH_TYPE_CHAR:
+ sum += (long)(unsigned char)AG_SCM_CHAR(car);
+ break;
+
+ case GH_TYPE_NUMBER:
+ sum += AG_SCM_TO_LONG(car);
+ break;
+
+ case GH_TYPE_STRING:
+ sum += strtol(ag_scm2zchars(car, "number-in-string"), NULL, 0);
+ }
+ } while (--len > 0);
+
+ return AG_SCM_FROM_LONG(sum);
+}
+
+
+/*=gfunc string_to_c_name_x
+ *
+ * what: map non-name chars to underscore
+ * general_use:
+ *
+ * exparg: str , input/output string
+ *
+ * doc: Change all the graphic characters that are invalid in a C name token
+ * into underscores. Whitespace characters are ignored. Any other
+ * character type (i.e. non-graphic and non-white) will cause a failure.
+=*/
+SCM
+ag_scm_string_to_c_name_x(SCM str)
+{
+ int len;
+ char* pz;
+
+ if (! AG_SCM_STRING_P(str))
+ scm_wrong_type_arg(STR_TO_C_NAME, 1, str);
+
+
+
+ for (pz = (char*)(void*)AG_SCM_CHARS(str), len = AG_SCM_STRLEN(str);
+ --len >= 0;
+ pz++) {
+
+ char ch = *pz;
+ if (IS_ALPHANUMERIC_CHAR(ch) || IS_WHITESPACE_CHAR(ch))
+ continue;
+
+ if (! IS_GRAPHIC_CHAR(ch))
+ scm_misc_error(STR_TO_C_NAME, STR_TO_C_MAP_FAIL, str);
+
+ *pz = '_';
+ }
+
+ return str;
+}
+
+
+/*=gfunc string_upcase_x
+ *
+ * what: make a string be upper case
+ * general_use:
+ *
+ * exparg: str , input/output string
+ *
+ * doc: Change to upper case all the characters in an SCM string.
+=*/
+SCM
+ag_scm_string_upcase_x(SCM str)
+{
+ int len;
+ char* pz;
+
+ if (! AG_SCM_STRING_P(str))
+ return SCM_UNDEFINED;
+
+ len = AG_SCM_STRLEN(str);
+ pz = (char*)(void*)AG_SCM_CHARS(str);
+ while (--len >= 0) {
+ char ch = *pz;
+ if (IS_LOWER_CASE_CHAR(ch))
+ *pz = toupper(ch);
+ pz++;
+ }
+
+ return str;
+}
+
+
+/*=gfunc string_upcase
+ *
+ * what: upper case a new string
+ * general_use:
+ *
+ * exparg: str , input string
+ *
+ * doc: Create a new SCM string containing the same text as the original,
+ * only all the lower case letters are changed to upper case.
+=*/
+SCM
+ag_scm_string_upcase(SCM str)
+{
+ SCM res;
+ if (! AG_SCM_STRING_P(str))
+ return SCM_UNDEFINED;
+
+ res = AG_SCM_STR2SCM(AG_SCM_CHARS(str), AG_SCM_STRLEN(str));
+ scm_string_upcase_x(res);
+ return res;
+}
+
+
+/*=gfunc string_capitalize_x
+ *
+ * what: capitalize a string
+ * general_use:
+ *
+ * exparg: str , input/output string
+ *
+ * doc: capitalize all the words in an SCM string.
+=*/
+SCM
+ag_scm_string_capitalize_x(SCM str)
+{
+ int len;
+ char* pz;
+ bool word_start = true;
+
+ if (! AG_SCM_STRING_P(str))
+ return SCM_UNDEFINED;
+
+ len = AG_SCM_STRLEN(str);
+ pz = (char*)(void*)AG_SCM_CHARS(str);
+
+ while (--len >= 0) {
+ char ch = *pz;
+
+ if (! IS_ALPHANUMERIC_CHAR(ch)) {
+ word_start = true;
+
+ } else if (word_start) {
+ word_start = false;
+ if (IS_LOWER_CASE_CHAR(ch))
+ *pz = toupper(ch);
+
+ } else if (IS_UPPER_CASE_CHAR(ch))
+ *pz = tolower(ch);
+
+ pz++;
+ }
+
+ return str;
+}
+
+
+/*=gfunc string_capitalize
+ *
+ * what: capitalize a new string
+ * general_use:
+ *
+ * exparg: str , input string
+ *
+ * doc: Create a new SCM string containing the same text as the original,
+ * only all the first letter of each word is upper cased and all
+ * other letters are made lower case.
+=*/
+SCM
+ag_scm_string_capitalize(SCM str)
+{
+ SCM res;
+ if (! AG_SCM_STRING_P(str))
+ return SCM_UNDEFINED;
+
+ res = AG_SCM_STR2SCM(AG_SCM_CHARS(str), AG_SCM_STRLEN(str));
+ ag_scm_string_capitalize_x(res);
+ return res;
+}
+
+
+/*=gfunc string_downcase_x
+ *
+ * what: make a string be lower case
+ * general_use:
+ *
+ * exparg: str , input/output string
+ *
+ * doc: Change to lower case all the characters in an SCM string.
+=*/
+SCM
+ag_scm_string_downcase_x(SCM str)
+{
+ int len;
+ char* pz;
+
+ if (! AG_SCM_STRING_P(str))
+ return SCM_UNDEFINED;
+
+ len = AG_SCM_STRLEN(str);
+ pz = (char*)(void*)AG_SCM_CHARS(str);
+ while (--len >= 0) {
+ char ch = *pz;
+ if (IS_UPPER_CASE_CHAR(ch))
+ *pz = tolower(ch);
+ pz++;
+ }
+
+ return str;
+}
+
+
+/*=gfunc string_downcase
+ *
+ * what: lower case a new string
+ * general_use:
+ *
+ * exparg: str , input string
+ *
+ * doc: Create a new SCM string containing the same text as the original,
+ * only all the upper case letters are changed to lower case.
+=*/
+SCM
+ag_scm_string_downcase(SCM str)
+{
+ SCM res;
+ if (! AG_SCM_STRING_P(str))
+ return SCM_UNDEFINED;
+
+ res = AG_SCM_STR2SCM(AG_SCM_CHARS(str), AG_SCM_STRLEN(str));
+ ag_scm_string_downcase_x(res);
+ return res;
+}
+
+
+/*=gfunc string_to_camelcase
+ *
+ * what: make a string be CamelCase
+ * general_use:
+ *
+ * exparg: str , input/output string
+ *
+ * doc: Capitalize the first letter of each block of letters and numbers,
+ * and stripping out characters that are not alphanumerics.
+ * For example, "alpha-beta0gamma" becomes "AlphaBeta0gamma".
+=*/
+SCM
+ag_scm_string_to_camelcase(SCM str)
+{
+ int len;
+ char* pzd, * res;
+ char* pzs;
+ bool cap_done = false;
+
+ if (! AG_SCM_STRING_P(str))
+ return SCM_UNDEFINED;
+
+ len = AG_SCM_STRLEN(str);
+ res = pzd = ag_scribble(len + 1);
+ pzs = (char*)(void*)AG_SCM_CHARS(str);
+
+ while (--len >= 0) {
+ unsigned int ch = *(pzs++);
+ if (IS_LOWER_CASE_CHAR(ch)) {
+ if (! cap_done) {
+ ch = toupper(ch);
+ cap_done = true;
+ }
+
+ } else if (IS_UPPER_CASE_CHAR(ch)) {
+ if (cap_done)
+ ch = tolower(ch);
+ else
+ cap_done = true;
+
+ } else if (IS_DEC_DIGIT_CHAR(ch)) {
+ cap_done = false;
+
+ } else {
+ cap_done = false;
+ continue;
+ }
+
+ *(pzd++) = ch;
+ }
+
+ return AG_SCM_STR2SCM(res, (pzd - res));
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expGuile.c */
diff --git a/agen5/expMake.c b/agen5/expMake.c
new file mode 100644
index 0000000..926b930
--- /dev/null
+++ b/agen5/expMake.c
@@ -0,0 +1,429 @@
+
+/**
+ * @file expMake.c
+ *
+ * Time-stamp: "2012-03-04 13:54:13 bkorb"
+ *
+ * This module implements Makefile construction functions.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Figure out how to handle the line continuation.
+ * If the line we just finished ends with a backslash, we're done.
+ * Just add the newline character. If it ends with a semi-colon or a
+ * doubled amphersand or doubled or-bar, then escape the newline with a
+ * backslash. If the line ends with one of the keywords "then", "in" or
+ * "else", also only add the escaped newline. Otherwise, add a
+ * semi-colon, backslash and newline.
+ *
+ * @param ppzi pointer to pointer to input text
+ * @param ppzo pointer to pointer to output text
+ * @param tabch line prefix (tab) character
+ * @param bol pointer to start of currently-being-output line
+ *
+ * @returns false to say the newline is dropped becase we're done
+ * true to say the line was appended with the newline.
+ */
+static bool
+handle_eol(char ** ppzi, char ** ppzo, char tabch, char * bol)
+{
+ char * pzScn = *ppzi;
+ char * pzOut = *ppzo;
+ int l_len = pzOut - bol;
+
+ /*
+ * Backup past trailing white space (other than newline).
+ */
+ while (IS_NON_NL_WHITE_CHAR(pzOut[-1]))
+ pzOut--;
+
+ /*
+ * Skip over empty lines, but leave leading white space
+ * on the next non-empty line.
+ */
+ {
+ char* pz = pzScn;
+ while (IS_WHITESPACE_CHAR(*pz)) {
+ if (*(pz++) == NL)
+ pzScn = pz;
+ }
+ }
+
+ /*
+ * The final newline is dropped.
+ */
+ if (*pzScn == NUL)
+ return false;
+
+ switch (pzOut[-1]) {
+ case '\\':
+ /*
+ * The newline is already escaped, so don't
+ * insert our extra command termination.
+ */
+ *(pzOut++) = NL;
+ break;
+
+ case '&':
+ /*
+ * A single ampersand is a backgrounded command. We must terminate
+ * those statements, but not statements conjoined with '&&'.
+ */
+ if ('&' != pzOut[-2])
+ goto append_statement_end;
+ /* FALLTHROUGH */
+
+ case '|':
+ case ';':
+ skip_semi_colon:
+ /*
+ * Whatever the reason for a final '|', '&' or ';',
+ * we will not add a semi-colon after it.
+ */
+ memcpy(pzOut, MAKE_SCRIPT_NL + 2, MAKE_SCRIPT_NL_LEN - 2);
+ pzOut += MAKE_SCRIPT_NL_LEN - 2;
+ break;
+
+ case 'n': // "then" or "in"
+ if (l_len < 3)
+ goto append_statement_end;
+
+ if (pzOut[-2] == 'i') {
+ if ((l_len == 3) || IS_WHITESPACE_CHAR(pzOut[-3]))
+ goto skip_semi_colon;
+ goto append_statement_end;
+ }
+
+ if ( (l_len < 5)
+ || ( (l_len > 5)
+ && ! IS_WHITESPACE_CHAR(pzOut[-5]) ))
+ goto append_statement_end;
+ if (strncmp(pzOut-4, HANDLE_EOL__THE, HANDLE_EOL__THE_LEN) == 0)
+ goto skip_semi_colon;
+ goto append_statement_end;
+
+ case 'e': // "else"
+ if ( (l_len < 5)
+ || ( (l_len > 5)
+ && ! IS_WHITESPACE_CHAR(pzOut[-5]) ))
+ goto append_statement_end;
+ if (strncmp(pzOut-4, HANDLE_EOL__ELS, HANDLE_EOL__ELS_LEN) == 0)
+ goto skip_semi_colon;
+ goto append_statement_end;
+
+ default:
+ append_statement_end:
+ /*
+ * Terminate the current command and escape the newline.
+ */
+ memcpy(pzOut, MAKE_SCRIPT_NL, MAKE_SCRIPT_NL_LEN);
+ pzOut += MAKE_SCRIPT_NL_LEN;
+ }
+
+ /*
+ * We have now started our next output line and there are still data.
+ * Indent with a tab, if called for. If we do insert a tab, then skip
+ * leading tabs on the line.
+ */
+ if (tabch) {
+ *(pzOut++) = tabch;
+ while (*pzScn == tabch) pzScn++;
+ }
+
+ *ppzi = pzScn;
+ *ppzo = pzOut;
+ return true;
+}
+
+/**
+ * Pass through untreated sedable lines. Sometimes it is just very useful
+ * to post-process Makefile files with sed(1) to clean it up.
+ *
+ * @param txt pointer to text. We skip initial white space.
+ * @param tab pointer to where we stash the tab character to use
+ * @returns true to say this was a sed line and was emitted,
+ * false to say it was not and needs to be copied out.
+ */
+static bool
+handle_sed_expr(char ** src_p, char ** out_p)
+{
+ char * src = *src_p;
+ char * out = *out_p;
+
+ switch (src[1]) {
+ case 'i':
+ if (strncmp(src+2, HANDLE_SED_IFNDEF, HANDLE_SED_IFNDEF_LEN) == 0)
+ break;
+ if (strncmp(src+2, HANDLE_SED_IFDEF, HANDLE_SED_IFDEF_LEN) == 0)
+ break;
+ return false;
+
+ case 'e':
+ if (strncmp(src+2, HANDLE_SED_ELSE, HANDLE_SED_ELSE_LEN) == 0)
+ break;
+ if (strncmp(src+2, HANDLE_SED_ENDIF, HANDLE_SED_ENDIF_LEN) == 0)
+ break;
+ /* FALLTHROUGH */
+ default:
+ return false;
+ }
+
+ {
+ char * p = BRK_NEWLINE_CHARS(src);
+ size_t l;
+ if (*p == NL) /* do not skip NUL */
+ p++;
+ l = p - src;
+ memcpy(out, src, l);
+ *src_p = src + l;
+ *out_p = out + l;
+ }
+
+ return true;
+}
+
+/**
+ * Compute a maximal size for the output script. Leading and trailing white
+ * space are trimmed. Dollar characters will likely be doubled and newlines
+ * may get as many as MAKE_SCRIPT_NL_LEN characters inserted. Make sure
+ * there's space.
+ *
+ * @param txt pointer to text. We skip initial white space.
+ * @param tab pointer to where we stash the tab character to use
+ * @returns the maximum number of bytes required to store result.
+ */
+static size_t
+script_size(char ** txt_p, char * tab)
+{
+ char * txt = *txt_p;
+ char * ptxte;
+ size_t sz = 0;
+
+ /*
+ * skip all blank lines and other initial white space
+ * in the source string.
+ */
+ if (! IS_WHITESPACE_CHAR(*txt))
+ *tab = TAB;
+ else {
+ txt = SPN_WHITESPACE_CHARS(txt + 1);
+ *tab = (txt[-1] == TAB) ? NUL : TAB;
+ }
+
+ /*
+ * Do nothing with empty input.
+ */
+ if (*txt == NUL)
+ return 0;
+
+ /*
+ * "txt" is now our starting point. Do not modify it any more.
+ */
+ *txt_p = txt;
+
+ for (ptxte = txt - 1;;) {
+ ptxte = BRK_MAKE_SCRIPT_CHARS(ptxte+1);
+ if (*ptxte == NUL)
+ break;
+ sz += (*ptxte == '$') ? 1 : MAKE_SCRIPT_NL_LEN;
+ }
+
+ ptxte = SPN_WHITESPACE_BACK(txt, ptxte);
+ *ptxte = NUL;
+ sz += (ptxte - txt);
+ return sz;
+}
+
+/*=gfunc makefile_script
+ *
+ * what: create makefile script
+ * general_use:
+ *
+ * exparg: text, the text of the script
+ *
+ * doc:
+ * This function will take ordinary shell script text and reformat it
+ * so that it will work properly inside of a makefile shell script.
+ * Not every shell construct can be supported; the intent is to have
+ * most ordinary scripts work without much, if any, alteration.
+ *
+ * The following transformations are performed on the source text:
+ *
+ * @enumerate
+ * @item
+ * Trailing whitespace on each line is stripped.
+ *
+ * @item
+ * Except for the last line, the string, " ; \\" is appended to the end of
+ * every line that does not end with certain special characters or keywords.
+ * Note that this will mutilate multi-line quoted strings, but @command{make}
+ * renders it impossible to use multi-line constructs anyway.
+ *
+ * @item
+ * If the line ends with a backslash, it is left alone.
+ *
+ * @item
+ * If the line ends with a semi-colon, conjunction operator, pipe (vertical
+ * bar) or one of the keywords "then", "else" or "in", then a space and a
+ * backslash is added, but no semi-colon.
+ *
+ * @item
+ * The dollar sign character is doubled, unless it immediately precedes an
+ * opening parenthesis or the single character make macros '*', '<', '@@',
+ * '?' or '%'. Other single character make macros that do not have enclosing
+ * parentheses will fail. For shell usage of the "$@@", "$?" and "$*"
+ * macros, you must enclose them with curly braces, e.g., "$@{?@}".
+ * The ksh construct @code{$(<command>)} will not work. Though some
+ * @command{make}s accept @code{$@{var@}} constructs, this function will
+ * assume it is for shell interpretation and double the dollar character.
+ * You must use @code{$(var)} for all @command{make} substitutions.
+ *
+ * @item
+ * Double dollar signs are replaced by four before the next character
+ * is examined.
+ *
+ * @item
+ * Every line is prefixed with a tab, unless the first line
+ * already starts with a tab.
+ *
+ * @item
+ * The newline character on the last line, if present, is suppressed.
+ *
+ * @item
+ * Blank lines are stripped.
+ *
+ * @item
+ * Lines starting with "@@ifdef", "@@ifndef", "@@else" and "@@endif" are
+ * presumed to be autoconf "sed" expression tags. These lines will be
+ * emitted as-is, with no tab prefix and no line splicing backslash.
+ * These lines can then be processed at configure time with
+ * @code{AC_CONFIG_FILES} sed expressions, similar to:
+ *
+ * @example
+ * sed "/^@@ifdef foo/d;/^@@endif foo/d;/^@@ifndef foo/,/^@@endif foo/d"
+ * @end example
+ * @end enumerate
+ *
+ * @noindent
+ * This function is intended to be used approximately as follows:
+ *
+ * @example
+ * $(TARGET) : $(DEPENDENCIES)
+ * <+ (out-push-new) +>
+ * ....mostly arbitrary shell script text....
+ * <+ (makefile-script (out-pop #t)) +>
+ * @end example
+=*/
+SCM
+ag_scm_makefile_script(SCM text_scm)
+{
+ char * res_str; /*@< result string */
+ char * out; /*@< output scanning ptr */
+ char * bol; /*@< start of last output line */
+ char tabch; /*@< char to use for start-of-line tab */
+
+ char * text = ag_scm2zchars(text_scm, "make script");
+ size_t sz = script_size(&text, &tabch);
+
+ if (sz == 0)
+ return AG_SCM_STR02SCM(zNil);
+
+ bol = out = res_str = ag_scribble(sz);
+
+ /*
+ * Force the initial line to start with a real tab.
+ */
+ *(out++) = TAB;
+
+ for (;;) {
+ char * p = BRK_MAKE_SCRIPT_CHARS(text);
+ size_t l = p - text;
+ if (l > 0) {
+ memcpy(out, text, l);
+ text = p;
+ out += l;
+ }
+
+ /*
+ * "text" now points to one of three characters:
+ * a newline, a dollar or a NUL byte.
+ */
+ if (*text == NUL)
+ break;
+
+ if (*text == NL) {
+ if (! handle_eol(&text, &out, tabch, bol))
+ break;
+
+ bol = out;
+
+ /*
+ * As a special "hack", if a line starts with "@ifdef", "@ifndef",
+ * "@else" or "@endif", then we assume post processing sed will
+ * fix it up. Those lines get left alone.
+ */
+ if (*text == '@') {
+ if (handle_sed_expr(&text, &out))
+ bol = out;
+ }
+
+ } else {
+ /*
+ * Quadruple a double dollar, leave alone make-interesting
+ * dollars, and double it otherwise.
+ */
+ switch (text[1]) {
+ case '(': case '*': case '@': case '<': case '%': case '?':
+ /* one only */
+ break;
+
+ case '$':
+ /*
+ * $$ in the shell means process id. Avoid having to do a
+ * backward scan on the second '$' by handling the next '$'
+ * now. We get FOUR '$' chars.
+ */
+ text++;
+ *(out++) = '$';
+ *(out++) = '$';
+ *(out++) = '$';
+ /* quadruple */
+ break;
+
+ default:
+ *(out++) = '$'; /* double */
+ }
+
+ *(out++) = *(text++);
+ }
+ }
+
+ {
+ SCM res = AG_SCM_STR2SCM(res_str, out - res_str);
+ return res;
+ }
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expMake.c */
diff --git a/agen5/expOutput.c b/agen5/expOutput.c
new file mode 100644
index 0000000..8404ce2
--- /dev/null
+++ b/agen5/expOutput.c
@@ -0,0 +1,934 @@
+
+/**
+ * @file expOutput.c
+ *
+ * Time-stamp: "2012-04-07 09:20:37 bkorb"
+ *
+ * This module implements the output file manipulation function
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef S_IAMB
+/*
+ * Access Mask Bits (3 special plus RWX for User Group & Others (9))
+ */
+# define S_IAMB (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
+#endif
+
+#define NO_WRITE_MASK ((unsigned)(~(S_IWUSR|S_IWGRP|S_IWOTH) & S_IAMB))
+
+typedef struct {
+ char const * pzSuspendName;
+ out_stack_t* pOutDesc;
+} tSuspendName;
+
+static int suspendCt = 0;
+static int suspAllocCt = 0;
+static tSuspendName* pSuspended = NULL;
+static int outputDepth = 1;
+
+/**
+ * return the current line number
+ */
+static int
+current_line(FILE * fp)
+{
+ int lnno = 1;
+
+ while (! feof(fp)) {
+ int ch = getc(fp);
+ if (ch == NL)
+ lnno++;
+ }
+
+ return lnno;
+}
+
+/**
+ * guts of the output file/line function
+ */
+static SCM
+do_output_file_line(int line_delta, char const * fmt)
+{
+ char * buf;
+ char const * fname = cur_fpstack->stk_fname;
+
+ if (cur_fpstack->stk_flags & FPF_TEMPFILE) {
+ fname = OUTPUT_TEMP_FILE;
+ line_delta = 0;
+
+ } else if (fseek(cur_fpstack->stk_fp, 0, SEEK_SET) == 0) {
+ line_delta += current_line(cur_fpstack->stk_fp);
+
+ } else {
+ line_delta = 0;
+ }
+
+ {
+ size_t sz = strlen(fmt) + strlen(fname) + 24;
+ buf = ag_scribble(sz);
+ }
+
+ {
+ void * args[2] = {
+ (void *)fname,
+ (void *)(uintptr_t)line_delta
+ };
+ sprintfv(buf, fmt, (snv_constpointer *)args);
+ }
+
+ return AG_SCM_STR02SCM(buf);
+}
+
+/**
+ * chmod a-w on a file descriptor.
+ */
+LOCAL void
+make_readonly(void)
+{
+#if defined(HAVE_FSTAT) || defined(HAVE_FCHMOD)
+ int fd = fileno(cur_fpstack->stk_fp);
+#endif
+ struct stat sbuf;
+
+ /*
+ * If the output is supposed to be writable, then also see if
+ * it is a temporary condition (set vs. a command line option).
+ */
+ if (ENABLED_OPT(WRITABLE)) {
+ if (! HAVE_OPT(WRITABLE))
+ CLEAR_OPT(WRITABLE);
+ return;
+ }
+
+ /*
+ * Set our usage mask to all all the access
+ * bits that do not provide for write access
+ */
+#ifdef HAVE_FSTAT
+ fstat(fd, &sbuf);
+#else
+ stat(cur_fpstack->stk_fname, &sbuf);
+#endif
+
+ /*
+ * Mask off the write permission bits, but ensure that
+ * the user read bit is set.
+ */
+ {
+ mode_t f_mode = (NO_WRITE_MASK & sbuf.st_mode) | S_IRUSR;
+
+#ifdef HAVE_FCHMOD
+ fchmod(fd, f_mode);
+#else
+ chmod(cur_fpstack->stk_fname, f_mode);
+#endif
+ }
+}
+
+/**
+ * Some common code for creating a new file
+ */
+LOCAL void
+open_output_file(char const * fname, size_t nmsz, char const * mode, int flags)
+{
+ char * pz;
+ out_stack_t* p = AGALOC(sizeof(*p), "out file stack");
+
+ pz = (char*)AGALOC(nmsz + 1, "file name string");
+ memcpy(pz, fname, nmsz);
+ pz[ nmsz ] = NUL;
+ memset(p, NUL, sizeof(*p));
+ p->stk_fname = pz;
+
+ /*
+ * IF we are creating the file and we are allowed to unlink the output,
+ * then start by unlinking the thing.
+ */
+ if ((*mode == 'w') && ((flags & FPF_NOUNLINK) == 0)) {
+ if ((unlink(pz) != 0) && (errno != ENOENT))
+ AG_CANT(OUTPUT_NO_UNLINK, pz);
+ }
+
+ /*
+ * If we cannot write to the file, try to change permissions.
+ */
+ if ( (access(fname, W_OK) != 0)
+ && (errno != ENOENT)) {
+ struct stat sbuf;
+ if (stat(fname, &sbuf) == 0) {
+ mode_t m = (sbuf.st_mode & 07777) | S_IWUSR;
+ chmod(fname, m);
+ }
+ }
+
+ p->stk_fp = fopen(pz, mode);
+ if (p->stk_fp == NULL)
+ AG_CANT(OUTPUT_NO_OPEN, pz);
+
+ p->stk_prev = cur_fpstack;
+ cur_fpstack = p;
+ p->stk_flags = FPF_FREE | flags;
+ outputDepth++;
+
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, TRACE_OPEN_OUT, __func__, fname, mode);
+
+ /*
+ * Avoid printing temporary file names in the dependency file
+ */
+ if ((dep_fp != NULL) && ((flags & FPF_TEMPFILE) == 0))
+ add_target_file(fname);
+}
+
+/*=gfunc out_delete
+ *
+ * what: delete current output file
+ * doc:
+ * Remove the current output file. Cease processing the template for
+ * the current suffix. It is an error if there are @code{push}-ed
+ * output files. Use the @code{(error "0")} scheme function instead.
+ * @xref{output controls}.
+=*/
+SCM
+ag_scm_out_delete(void)
+{
+ /*
+ * Delete the current output file
+ */
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, TRACE_OUT_DELETE, cur_fpstack->stk_fname);
+ rm_target_file(cur_fpstack->stk_fname);
+ outputDepth = 1;
+ longjmp(abort_jmp_buf, PROBLEM);
+ /* NOTREACHED */
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc out_move
+ *
+ * what: change name of output file
+ * exparg: new-name, new name for the current output file
+ *
+ * doc:
+ *
+ * Rename current output file. @xref{output controls}.
+ * Please note: changing the name will not save a temporary file from
+ * being deleted. It @i{may}, however, be used on the root output file.
+ *
+ * NOTE: if you are creating a dependency file, @i{both} the original
+ * file name @i{and} the new file name will be listed as derived files.
+=*/
+SCM
+ag_scm_out_move(SCM new_file)
+{
+ size_t sz = AG_SCM_STRLEN(new_file);
+ char* pz = (char*)AGALOC(sz + 1, "new file name string");
+ memcpy(pz, AG_SCM_CHARS(new_file), sz);
+ pz[ sz ] = NUL;
+
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, TRACE_MOVE_FMT, __func__,
+ cur_fpstack->stk_fname, pz);
+ rename(cur_fpstack->stk_fname, pz);
+
+ if (dep_fp != NULL) {
+ rm_target_file(cur_fpstack->stk_fname);
+ add_target_file(pz);
+ }
+
+ if ((cur_fpstack->stk_flags & FPF_STATIC_NM) == 0)
+ AGFREE((void*)cur_fpstack->stk_fname);
+
+ AGDUPSTR(cur_fpstack->stk_fname, pz, "file name");
+ cur_fpstack->stk_flags &= ~FPF_STATIC_NM;
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc out_pop
+ *
+ * what: close current output file
+ * exparg: disp, return contents of the file, optional
+ * doc:
+ * If there has been a @code{push} on the output, then close that
+ * file and go back to the previously open file. It is an error
+ * if there has not been a @code{push}. @xref{output controls}.
+ *
+ * If there is no argument, no further action is taken. Otherwise,
+ * the argument should be @code{#t} and the contents of the file
+ * are returned by the function.
+=*/
+SCM
+ag_scm_out_pop(SCM ret_contents)
+{
+ SCM res = SCM_UNDEFINED;
+
+ if (cur_fpstack->stk_prev == NULL)
+ AG_ABEND(SCM_OUT_POP_EMPTY);
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_POP_FMT, __func__, cur_fpstack->stk_fname,
+ (ret_contents == SCM_UNDEFINED) ? "" : " #t");
+
+ if (AG_SCM_BOOL_P(ret_contents) && AG_SCM_NFALSEP(ret_contents)) {
+ long pos = ftell(cur_fpstack->stk_fp);
+ char * pz;
+
+ if (pos <= 0) {
+ pz = (void*)zNil; // const-ness not important
+ pos = 0;
+
+ } else {
+ pz = ag_scribble((size_t)pos + 1);
+ rewind(cur_fpstack->stk_fp);
+ if (fread(pz, (size_t)pos, (size_t)1, cur_fpstack->stk_fp) != 1)
+ AG_CANT(SCM_OUT_POP_NO_REREAD, cur_fpstack->stk_fname);
+ }
+
+ res = AG_SCM_STR2SCM(pz, pos);
+ }
+
+ outputDepth--;
+ out_close(false);
+ return res;
+}
+
+/*=gfunc output_file_next_line
+ *
+ * what: print the file name and next line number
+ *
+ * exparg: line_off, offset to line number, optional
+ * exparg: alt_fmt, alternate format string, optional
+ *
+ * doc:
+ * Returns a string with the current output file name and line number.
+ * The default format is: # <line+1> "<output-file-name>" The argument may be
+ * either a number indicating an offset from the current output line number
+ * or an alternate formatting string. If both are provided, then the first
+ * must be a numeric offset.
+ *
+ * Be careful that you are directing output to the final output file.
+ * Otherwise, you will get the file name and line number of the temporary
+ * file. That won't be what you want.
+=*/
+SCM
+ag_scm_output_file_next_line(SCM num_or_str, SCM str)
+{
+ char const * pzFmt = FILE_LINE_FMT;
+ int line_off = 1;
+
+ if (AG_SCM_NUM_P(num_or_str))
+ line_off = AG_SCM_TO_LONG(num_or_str);
+ else
+ str = num_or_str;
+
+ if (AG_SCM_STRING_P(str))
+ pzFmt = ag_scm2zchars(str, "file/line format");
+
+ return do_output_file_line(line_off, pzFmt);
+}
+
+
+/*=gfunc out_suspend
+ *
+ * what: suspend current output file
+ * exparg: suspName, A name tag for reactivating
+ *
+ * doc:
+ * If there has been a @code{push} on the output, then set aside the output
+ * descriptor for later reactiviation with @code{(out-resume "xxx")}. The
+ * tag name need not reflect the name of the output file. In fact, the
+ * output file may be an anonymous temporary file. You may also change the
+ * tag every time you suspend output to a file, because the tag names are
+ * forgotten as soon as the file has been "resumed".
+=*/
+SCM
+ag_scm_out_suspend(SCM susp_nm)
+{
+ if (cur_fpstack->stk_prev == NULL)
+ AG_ABEND(OUT_SUSPEND_CANNOT);
+
+ if (++suspendCt > suspAllocCt) {
+ suspAllocCt += 8;
+ if (pSuspended == NULL)
+ pSuspended = (tSuspendName*)
+ AGALOC(suspAllocCt * sizeof(tSuspendName), "susp file list");
+ else
+ pSuspended = (tSuspendName*)
+ AGREALOC((void*)pSuspended,
+ suspAllocCt * sizeof(tSuspendName), "add to susp f");
+ }
+
+ pSuspended[ suspendCt-1 ].pzSuspendName = AG_SCM_TO_NEWSTR(susp_nm);
+ pSuspended[ suspendCt-1 ].pOutDesc = cur_fpstack;
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_SUSPEND, __func__, cur_fpstack->stk_fname,
+ pSuspended[ suspendCt-1 ].pzSuspendName);
+
+ cur_fpstack = cur_fpstack->stk_prev;
+ outputDepth--;
+
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc out_resume
+ *
+ * what: resume suspended output file
+ * exparg: susp_nm, A name tag for reactivating
+ * doc:
+ * If there has been a suspended output, then make that output descriptor
+ * current again. That output must have been suspended with the same tag
+ * name given to this routine as its argument.
+=*/
+SCM
+ag_scm_out_resume(SCM susp_nm)
+{
+ int ix = 0;
+ char const * pzName = ag_scm2zchars(susp_nm, "resume name");
+
+ for (; ix < suspendCt; ix++) {
+ if (strcmp(pSuspended[ ix ].pzSuspendName, pzName) == 0) {
+ pSuspended[ ix ].pOutDesc->stk_prev = cur_fpstack;
+ cur_fpstack = pSuspended[ ix ].pOutDesc;
+ free((void*)pSuspended[ ix ].pzSuspendName); /* Guile alloc */
+ if (ix < --suspendCt)
+ pSuspended[ ix ] = pSuspended[ suspendCt ];
+ ++outputDepth;
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_RESUME, __func__,
+ cur_fpstack->stk_fname, pzName);
+ return SCM_UNDEFINED;
+ }
+ }
+
+ AG_ABEND(aprf(OUT_RESUME_CANNOT, pzName));
+ /* NOTREACHED */
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc out_emit_suspended
+ *
+ * what: emit the text of suspended output
+ * exparg: susp_nm, A name tag of suspended output
+ * doc:
+ * This function is equivalent to
+ * @code{(begin (out-resume <name>) (out-pop #t))}
+=*/
+SCM
+ag_scm_out_emit_suspended(SCM susp_nm)
+{
+ (void)ag_scm_out_resume(susp_nm);
+ return ag_scm_out_pop(SCM_BOOL_T);
+}
+
+
+/*=gfunc ag_fprintf
+ *
+ * what: format to autogen stream
+ *
+ * exparg: ag-diversion, AutoGen diversion name or number
+ * exparg: format, formatting string
+ * exparg: format-arg, list of arguments to formatting string, opt, list
+ *
+ * doc: Format a string using arguments from the alist.
+ * Write to a specified AutoGen diversion.
+ * That may be either a specified suspended output stream
+ * (@pxref{SCM out-suspend}) or an index into the output stack
+ * (@pxref{SCM out-push-new}). @code{(ag-fprintf 0 ...)} is
+ * equivalent to @code{(emit (sprintf ...))}, and
+ * @code{(ag-fprintf 1 ...)} sends output to the most recently
+ * suspended output stream.
+=*/
+SCM
+ag_scm_ag_fprintf(SCM port, SCM fmt, SCM alist)
+{
+ int list_len = scm_ilength(alist);
+ char const * pzFmt = ag_scm2zchars(fmt, WORD_FORMAT);
+ SCM res = run_printf(pzFmt, list_len, alist);
+
+ /*
+ * If "port" is a string, it must match one of the suspended outputs.
+ * Otherwise, we'll fall through to the abend.
+ */
+ if (AG_SCM_STRING_P(port)) {
+ int ix = 0;
+ char const * pzName = ag_scm2zchars(port, "resume name");
+
+ for (; ix < suspendCt; ix++) {
+ if (strcmp(pSuspended[ ix ].pzSuspendName, pzName) == 0) {
+ out_stack_t* pSaveFp = cur_fpstack;
+ cur_fpstack = pSuspended[ ix ].pOutDesc;
+ (void) ag_scm_emit(res);
+ cur_fpstack = pSaveFp;
+ return SCM_UNDEFINED;
+ }
+ }
+ }
+
+ /*
+ * If "port" is a number, it is an index into the output stack with "0"
+ * (zero) representing the current output and "1" the last suspended
+ * output. If the number is out of range, we'll fall through to the
+ * abend.
+ */
+ else if (AG_SCM_NUM_P(port)) {
+ out_stack_t* pSaveFp = cur_fpstack;
+ unsigned long val = AG_SCM_TO_ULONG(port);
+
+ for (; val > 0; val--) {
+ cur_fpstack = cur_fpstack->stk_prev;
+ if (cur_fpstack == NULL) {
+ cur_fpstack = pSaveFp;
+ goto fprintf_woops;
+ }
+ }
+
+ (void) ag_scm_emit(res);
+ cur_fpstack = pSaveFp;
+ return SCM_UNDEFINED;
+ }
+
+ /*
+ * Still here? We have a bad "port" specification.
+ */
+ fprintf_woops:
+
+ AG_ABEND(AG_FPRINTF_BAD_PORT);
+ /* NOTREACHED */
+ return SCM_UNDEFINED;
+}
+
+/*=gfunc out_push_add
+ *
+ * what: append output to file
+ * exparg: file-name, name of the file to append text to
+ *
+ * doc:
+ * Identical to @code{push-new}, except the contents are @strong{not}
+ * purged, but appended to. @xref{output controls}.
+=*/
+SCM
+ag_scm_out_push_add(SCM new_file)
+{
+ static char const append_mode[] = "a" FOPEN_BINARY_FLAG "+";
+
+ if (! AG_SCM_STRING_P(new_file))
+ AG_ABEND(OUT_ADD_INVALID);
+
+ open_output_file(AG_SCM_CHARS(new_file), AG_SCM_STRLEN(new_file),
+ append_mode, 0);
+
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc make_tmp_dir
+ *
+ * what: create a temporary directory
+ *
+ * doc:
+ * Create a directory that will be cleaned up upon exit.
+=*/
+SCM
+ag_scm_make_tmp_dir(void)
+{
+ if (pz_temp_tpl == NULL) {
+ char * tmpdir = shell_cmd(MK_TMP_DIR_CMD);
+ char * cmdbf =
+ ag_scribble(SET_TMP_DIR_CMD_LEN + 2 * MK_TMP_DIR_CMD_LEN);
+
+ pz_temp_tpl = tmpdir;
+ temp_tpl_dir_len = strlen(tmpdir) - 9; // "ag-XXXXXX"
+
+ tmpdir[temp_tpl_dir_len - 1] = NUL; // trim dir char
+ sprintf(cmdbf, SET_TMP_DIR_CMD, tmpdir);
+ tmpdir[temp_tpl_dir_len - 1] = DIRCH; // restore dir char
+
+ ag_scm_c_eval_string_from_file_line(cmdbf, __FILE__, __LINE__);
+ }
+
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc out_push_new
+ *
+ * what: purge and create output file
+ * exparg: file-name, name of the file to create, optional
+ *
+ * doc:
+ * Leave the current output file open, but purge and create
+ * a new file that will remain open until a @code{pop} @code{delete}
+ * or @code{switch} closes it. The file name is optional and, if omitted,
+ * the output will be sent to a temporary file that will be deleted when
+ * it is closed.
+ * @xref{output controls}.
+=*/
+SCM
+ag_scm_out_push_new(SCM new_file)
+{
+ static char const write_mode[] = "w" FOPEN_BINARY_FLAG "+";
+
+ if (AG_SCM_STRING_P(new_file)) {
+ open_output_file(AG_SCM_CHARS(new_file), AG_SCM_STRLEN(new_file),
+ write_mode, 0);
+ return SCM_UNDEFINED;
+ }
+
+ /*
+ * "ENABLE_FMEMOPEN" is defined if we have either fopencookie(3GNU) or
+ * funopen(3BSD) is available and autogen was not configured with fmemopen
+ * disabled. We cannot use the POSIX fmemopen.
+ */
+#if defined(ENABLE_FMEMOPEN)
+ if (! HAVE_OPT(NO_FMEMOPEN)) {
+ char * pzNewFile;
+ out_stack_t * p;
+
+ /*
+ * This block is used IFF ENABLE_FMEMOPEN is defined and if
+ * --no-fmemopen is *not* selected on the command line.
+ */
+ p = (out_stack_t*)AGALOC(sizeof(out_stack_t), "out file stack");
+ p->stk_prev = cur_fpstack;
+ p->stk_flags = FPF_FREE;
+ p->stk_fp = ag_fmemopen(NULL, (ssize_t)0, "w" FOPEN_BINARY_FLAG "+");
+ pzNewFile = (char*)MEM_FILE_STR;
+ p->stk_flags |= FPF_STATIC_NM | FPF_NOUNLINK | FPF_NOCHMOD;
+
+ if (p->stk_fp == NULL)
+ AG_CANT(OUT_PUSH_NEW_FAIL, pzNewFile);
+
+ p->stk_fname = pzNewFile;
+ outputDepth++;
+ cur_fpstack = p;
+
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, TRACE_OUT_PUSH_NEW, __func__, pzNewFile);
+ return SCM_UNDEFINED;
+ }
+#endif
+
+ /*
+ * Either --no-fmemopen was specified or we cannot use ag_fmemopen().
+ */
+ {
+ static size_t tmplen;
+ char * tmp_fnm;
+ int tmpfd;
+
+ if (pz_temp_tpl == NULL)
+ ag_scm_make_tmp_dir();
+
+ tmplen = temp_tpl_dir_len + 10;
+ tmp_fnm = ag_scribble(tmplen + 1);
+ memcpy(tmp_fnm, pz_temp_tpl, tmplen + 1);
+ tmpfd = mkstemp(tmp_fnm);
+
+ if (tmpfd < 0)
+ AG_ABEND(aprf(OUT_PUSH_NEW_FAILED, pz_temp_tpl));
+
+ open_output_file(tmp_fnm, tmplen, write_mode, FPF_TEMPFILE);
+ close(tmpfd);
+ }
+
+ return SCM_UNDEFINED;
+}
+
+/*=gfunc out_switch
+ *
+ * what: close and create new output
+ * exparg: file-name, name of the file to create
+ *
+ * doc:
+ * Switch output files - close current file and make the current
+ * file pointer refer to the new file. This is equivalent to
+ * @code{out-pop} followed by @code{out-push-new}, except that
+ * you may not pop the base level output file, but you may
+ * @code{switch} it. @xref{output controls}.
+=*/
+SCM
+ag_scm_out_switch(SCM new_file)
+{
+ struct utimbuf tbuf;
+ char* pzNewFile;
+
+ if (! AG_SCM_STRING_P(new_file))
+ return SCM_UNDEFINED;
+ {
+ size_t sz = AG_SCM_STRLEN(new_file);
+ pzNewFile = AGALOC(sz + 1, "new file name");
+ memcpy(pzNewFile, AG_SCM_CHARS(new_file), sz);
+ pzNewFile[ sz ] = NUL;
+ }
+
+ /*
+ * IF no change, THEN ignore this
+ */
+ if (strcmp(cur_fpstack->stk_fname, pzNewFile) == 0) {
+ AGFREE((void*)pzNewFile);
+ return SCM_UNDEFINED;
+ }
+
+ make_readonly();
+
+ /*
+ * Make sure we get a new file pointer!!
+ * and try to ensure nothing is in the way.
+ */
+ unlink(pzNewFile);
+ if ( freopen(pzNewFile, "w" FOPEN_BINARY_FLAG "+", cur_fpstack->stk_fp)
+ != cur_fpstack->stk_fp)
+
+ AG_CANT(OUT_SWITCH_FAIL, pzNewFile);
+
+ /*
+ * Set the mod time on the old file.
+ */
+ tbuf.actime = time(NULL);
+ tbuf.modtime = outfile_time;
+ utime(cur_fpstack->stk_fname, &tbuf);
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, TRACE_OUT_SWITCH,
+ __func__, cur_fpstack->stk_fname, pzNewFile);
+ cur_fpstack->stk_fname = pzNewFile; /* memory leak */
+
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc out_depth
+ *
+ * what: output file stack depth
+ * doc: Returns the depth of the output file stack.
+ * @xref{output controls}.
+=*/
+SCM
+ag_scm_out_depth(void)
+{
+ return AG_SCM_INT2SCM(outputDepth);
+}
+
+
+/*=gfunc out_name
+ *
+ * what: current output file name
+ * doc: Returns the name of the current output file. If the current file
+ * is a temporary, unnamed file, then it will scan up the chain until
+ * a real output file name is found.
+ * @xref{output controls}.
+=*/
+SCM
+ag_scm_out_name(void)
+{
+ out_stack_t* p = cur_fpstack;
+ while (p->stk_flags & FPF_UNLINK) p = p->stk_prev;
+ return AG_SCM_STR02SCM((void*)p->stk_fname);
+}
+
+
+/*=gfunc out_line
+ *
+ * what: output file line number
+ * doc: Returns the current line number of the output file.
+ * It rewinds and reads the file to count newlines.
+=*/
+SCM
+ag_scm_out_line(void)
+{
+ int lineNum = 1;
+
+ do {
+ long svpos = ftell(cur_fpstack->stk_fp);
+ long pos = svpos;
+
+ if (pos == 0)
+ break;
+
+ rewind(cur_fpstack->stk_fp);
+ do {
+ int ich = fgetc(cur_fpstack->stk_fp);
+ unsigned char ch = ich;
+ if (ich < 0)
+ break;
+ if (ch == (unsigned char)NL)
+ lineNum++;
+ } while (--pos > 0);
+ fseek(cur_fpstack->stk_fp, svpos, SEEK_SET);
+ } while(0);
+
+ return AG_SCM_INT2SCM(lineNum);
+}
+
+#if 0 /* for compilers that do not like C++ comments... */
+// This is done so that comment delimiters can be included in the doc.
+//
+// /*=gfunc make_header_guard
+// *
+// * what: make self-inclusion guard
+// *
+// * exparg: name , header group name
+// *
+// * doc:
+// * This function will create a @code{#ifndef}/@code{#define}
+// * sequence for protecting a header from multiple evaluation.
+// * It will also set the Scheme variable @code{header-file}
+// * to the name of the file being protected and it will set
+// * @code{header-guard} to the name of the @code{#define} being
+// * used to protect it. It is expected that this will be used
+// * as follows:
+// * @example
+// * [+ (make-header-guard "group_name") +]
+// * ...
+// * #endif /* [+ (. header-guard) +] */
+// *
+// * #include "[+ (. header-file) +]"
+// * @end example
+// * @noindent
+// * The @code{#define} name is composed as follows:
+// *
+// * @enumerate
+// * @item
+// * The first element is the string argument and a separating underscore.
+// * @item
+// * That is followed by the name of the header file with illegal
+// * characters mapped to underscores.
+// * @item
+// * The end of the name is always, "@code{_GUARD}".
+// * @item
+// * Finally, the entire string is mapped to upper case.
+// * @end enumerate
+// *
+// * The final @code{#define} name is stored in an SCM symbol named
+// * @code{header-guard}. Consequently, the concluding @code{#endif} for the
+// * file should read something like:
+// *
+// * @example
+// * #endif /* [+ (. header-guard) +] */
+// * @end example
+// *
+// * The name of the header file (the current output file) is also stored
+// * in an SCM symbol, @code{header-file}. Therefore, if you are also
+// * generating a C file that uses the previously generated header file,
+// * you can put this into that generated file:
+// *
+// * @example
+// * #include "[+ (. header-file) +]"
+// * @end example
+// *
+// * Obviously, if you are going to produce more than one header file from
+// * a particular template, you will need to be careful how these SCM symbols
+// * get handled.
+// =*/
+#endif
+SCM
+ag_scm_make_header_guard(SCM name)
+{
+
+ char const * opz; // output file name string
+ size_t osz;
+
+ char const * gpz; // guard name string
+ size_t gsz;
+
+ {
+ out_stack_t* p = cur_fpstack;
+ while (p->stk_flags & FPF_UNLINK) p = p->stk_prev;
+ opz = p->stk_fname;
+ osz = strlen(opz);
+ }
+
+ /*
+ * Construct the gard name using the leader (passed in or "HEADER")
+ * and the trailer (always "_GUARD") and the output file name in between.
+ */
+ {
+ /*
+ * Leader string and its length. Usually passed, but defaults
+ * to "HEADER"
+ */
+ char const * lpz =
+ AG_SCM_STRING_P(name) ? AG_SCM_CHARS(name) : HEADER_STR;
+ size_t lsz = (lpz == HEADER_STR)
+ ? HEADER_STR_LEN : AG_SCM_STRLEN(name);
+
+ /*
+ * Full, maximal length of output
+ */
+ size_t hsz = lsz + osz + GUARD_SFX_LEN + 2;
+ char * scan_p;
+
+ /*
+ * Sanity:
+ */
+ if (*lpz == NUL) {
+ lpz = HEADER_STR;
+ lsz = HEADER_STR_LEN;
+ hsz += lsz;
+ }
+ scan_p = AGALOC(hsz, "header guard string");
+
+ gpz = scan_p; // gpz must be freed
+ do {
+ *(scan_p++) = toupper(*(lpz++));
+ } while (--lsz > 0);
+
+ /*
+ * This copy converts non-alphanumerics to underscores,
+ * but never inserts more than one at a time. Thus, we may
+ * not use all of the space in "gpz".
+ */
+ lpz = opz;
+ do {
+ *(scan_p++) = '_';
+ lpz = BRK_ALPHANUMERIC_CHARS(lpz);
+ while (IS_ALPHANUMERIC_CHAR(*lpz))
+ *(scan_p++) = toupper((unsigned)*(lpz++));
+ } while (*lpz != NUL);
+
+ memcpy(scan_p, GUARD_SFX, GUARD_SFX_LEN + 1);
+ gsz = (scan_p - gpz) + GUARD_SFX_LEN;
+ }
+
+ {
+ size_t sz1 = MK_HEAD_GUARD_SCM_LEN + gsz + osz;
+ size_t sz2 = MK_HEAD_GUARD_GUARD_LEN + 2 * gsz;
+ size_t sz = (sz1 < sz2) ? sz2 : sz1;
+ char * p = ag_scribble(sz);
+ sprintf(p, MK_HEAD_GUARD_SCM, opz, gpz);
+ (void)ag_scm_c_eval_string_from_file_line(p, __FILE__, __LINE__);
+
+ sprintf(p, MK_HEAD_GUARD_GUARD, gpz);
+ name = AG_SCM_STR02SCM(p);
+ }
+
+ AGFREE(gpz);
+ return (name);
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expOutput.c */
diff --git a/agen5/expPrint.c b/agen5/expPrint.c
new file mode 100644
index 0000000..472a03a
--- /dev/null
+++ b/agen5/expPrint.c
@@ -0,0 +1,344 @@
+
+/**
+ * @file expPrint.c
+ *
+ * Time-stamp: "2012-03-04 19:45:59 bkorb"
+ *
+ * The following code is necessary because the user can give us
+ * a printf format requiring a string pointer yet fail to provide
+ * a valid pointer, thus it will fault. This code protects
+ * against the fault so an error message can be emitted instead of
+ * a core dump :-)
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DEBUG_ENABLED
+static sigjmp_buf printJumpEnv;
+static int printJumpSignal = 0;
+#endif
+
+/* = = = START-STATIC-FORWARD = = = */
+static ssize_t
+safePrintf(char** ppzBuf, char const * pzFmt, void** argV);
+/* = = = END-STATIC-FORWARD = = = */
+
+#ifndef DEBUG_ENABLED
+ static void printFault(int sig)
+{
+ printJumpSignal = sig;
+ siglongjmp(printJumpEnv, sig);
+}
+#endif
+
+static ssize_t
+safePrintf(char** ppzBuf, char const * pzFmt, void** argV)
+{
+#if ! defined(DEBUG_ENABLED)
+ /*
+ * In normal operation (or during AutoGen testing), seg faults during the
+ * printf operation are caused by bad input data. During AutoGen
+ * development, we do not supply bad printf arguments, so we want to
+ * capture any segfaults when they happen with the correct stack trace.
+ * Therefore, during AutoGen development, we do not protect against seg
+ * faults.
+ */
+ struct sigaction saSave1;
+ struct sigaction saSave2;
+
+ {
+ struct sigaction sa;
+ sa.sa_handler = printFault;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
+ sigaction(SIGBUS, &sa, &saSave1);
+ sigaction(SIGSEGV, &sa, &saSave2);
+ }
+
+ /*
+ * IF the sprintfv call below is going to address fault,
+ * THEN ...
+ */
+ if (sigsetjmp(printJumpEnv, 0) != 0) {
+#ifndef HAVE_STRSIGNAL
+ extern char* strsignal(int signo);
+#endif
+ /*
+ * IF the fprintf command in the then clause has not failed yet,
+ * THEN perform that fprintf
+ */
+ if (sigsetjmp(printJumpEnv, 0) == 0)
+ fprintf(trace_fp, SAFE_PRINTF_BAD_FMT, ag_pname,
+ strsignal(printJumpSignal), pzFmt);
+
+ /*
+ * The "sprintfv" command below faulted, so we exit
+ */
+ AG_ABEND(SAFE_PRINTF_BAD_ARGS);
+ }
+#endif /* ! defined(DEBUG_ENABLED) */
+
+ {
+ int printSize = asprintfv(ppzBuf, pzFmt, (snv_constpointer*)argV);
+ if ((printSize & ~0xFFFFFU) != 0) /* 1MB max */
+ AG_ABEND(aprf(ASPRINTFV_FAIL_FMT, printSize));
+
+#if ! defined(DEBUG_ENABLED)
+ sigaction(SIGBUS, &saSave1, NULL);
+ sigaction(SIGSEGV, &saSave2, NULL);
+#endif
+ return printSize;
+ }
+}
+
+
+LOCAL SCM
+run_printf(char const * pzFmt, int len, SCM alist)
+{
+ SCM res;
+ void* args[8];
+ void** arglist;
+ void** argp;
+
+ if (len < 8)
+ arglist = argp = args;
+ else
+ arglist =
+ argp = (void**)malloc((len+1) * sizeof(void*));
+
+ while (len-- > 0) {
+ SCM car = SCM_CAR(alist);
+ alist = SCM_CDR(alist);
+ switch (ag_scm_type_e(car)) {
+ default:
+ case GH_TYPE_UNDEFINED:
+ *(argp++) = (char*)RUN_PRINTF_HUH;
+ break;
+
+ case GH_TYPE_BOOLEAN:
+ *(argp++) = (void*)((car == SCM_BOOL_F)
+ ? SCM_FALSE_STR : SCM_TRUE_STR);
+ break;
+
+ case GH_TYPE_CHAR:
+ *(char*)(argp++) = AG_SCM_CHAR(car);
+ break;
+
+ case GH_TYPE_PAIR:
+ *(argp++) = (char*)(SCM_LIST_STR+1);
+ break;
+
+ case GH_TYPE_NUMBER:
+ *(unsigned long*)(argp++) = AG_SCM_TO_ULONG(car);
+ break;
+
+ case GH_TYPE_SYMBOL:
+ case GH_TYPE_STRING:
+ *(argp++) = ag_scm2zchars(car, "printf str");
+ break;
+
+ case GH_TYPE_PROCEDURE:
+ *(argp++) = (char*)SCM_PROC_CAST;
+ break;
+
+ case GH_TYPE_VECTOR:
+ case GH_TYPE_LIST:
+ *(argp++) = (char*)SCM_LIST_STR;
+ break;
+ }
+ }
+
+ /*
+ * Do the formatting and allocate a new SCM to hold the result.
+ * Free up any allocations made by ``gh_scm2newstr''
+ */
+ {
+ char* pzBuf;
+ size_t bfSize = safePrintf(&pzBuf, pzFmt, arglist);
+ res = AG_SCM_STR2SCM(pzBuf, bfSize);
+ free(pzBuf);
+ }
+
+ if (arglist != args)
+ AGFREE((void*)arglist);
+
+ return res;
+}
+
+
+/*=gfunc sprintf
+ *
+ * what: format a string
+ * general_use:
+ *
+ * exparg: format, formatting string
+ * exparg: format-arg, list of arguments to formatting string, opt, list
+ *
+ * doc: Format a string using arguments from the alist.
+=*/
+SCM
+ag_scm_sprintf(SCM fmt, SCM alist)
+{
+ int list_len = scm_ilength(alist);
+ char* pzFmt = ag_scm2zchars(fmt, WORD_FORMAT);
+
+ if (list_len <= 0)
+ return fmt;
+
+ return run_printf(pzFmt, list_len, alist);
+}
+
+
+/*=gfunc printf
+ *
+ * what: format to stdout
+ * general_use:
+ *
+ * exparg: format, formatting string
+ * exparg: format-arg, list of arguments to formatting string, opt, list
+ *
+ * doc: Format a string using arguments from the alist.
+ * Write to the standard out port. The result will NOT appear in your
+ * output. Use this to print information messages to a template user.
+ * Use ``(sprintf ...)'' to add text to your document.
+=*/
+SCM
+ag_scm_printf(SCM fmt, SCM alist)
+{
+ int list_len = scm_ilength(alist);
+ char* pzFmt = ag_scm2zchars(fmt, WORD_FORMAT);
+
+ AG_SCM_DISPLAY(run_printf(pzFmt, list_len, alist));
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc fprintf
+ *
+ * what: format to a file
+ * general_use:
+ *
+ * exparg: port, Guile-scheme output port
+ * exparg: format, formatting string
+ * exparg: format-arg, list of arguments to formatting string, opt, list
+ *
+ * doc: Format a string using arguments from the alist.
+ * Write to a specified port. The result will NOT appear in your
+ * output. Use this to print information messages to a template user.
+=*/
+SCM
+ag_scm_fprintf(SCM port, SCM fmt, SCM alist)
+{
+ int list_len = scm_ilength(alist);
+ char* pzFmt = ag_scm2zchars(fmt, WORD_FORMAT);
+ SCM res = run_printf(pzFmt, list_len, alist);
+
+ return scm_display(res, port);
+}
+
+
+/*=gfunc hide_email
+ *
+ * what: convert eaddr to javascript
+ * general_use:
+ *
+ * exparg: display, display text
+ * exparg: eaddr, email address
+ *
+ * doc: Hides an email address as a java scriptlett.
+ * The 'mailto:' tag and the email address are coded bytes
+ * rather than plain text. They are also broken up.
+=*/
+SCM
+ag_scm_hide_email(SCM display, SCM eaddr)
+{
+ char* pzDisp = ag_scm2zchars(display, "fmt");
+ char* pzEadr = ag_scm2zchars(eaddr, "fmt");
+ size_t st_len = HIDE_EMAIL_START_STR_LEN;
+ size_t end_len = HIDE_EMAIL_END_FMT_LEN;
+
+ size_t str_size = (strlen(pzEadr) * HTML_DEC_DIGIT_LEN)
+ + st_len + end_len + strlen(pzDisp);
+
+ char* pzRes = ag_scribble(str_size);
+ char* pzScan = pzRes;
+
+ memcpy(pzScan, HIDE_EMAIL_START_STR, st_len);
+ pzScan += st_len;
+
+ for (;;) {
+ if (*pzEadr == NUL)
+ break;
+ pzScan += sprintf(pzScan, HTML_DEC_DIGIT, *(pzEadr++));
+ }
+
+ pzScan += sprintf(pzScan, HIDE_EMAIL_END_FMT, pzDisp);
+
+ return AG_SCM_STR2SCM(pzRes, (size_t)(pzScan - pzRes));
+}
+
+/*=gfunc format_arg_count
+ *
+ * what: count the args to a format
+ * general_use:
+ *
+ * exparg: format, formatting string
+ *
+ * doc: "Sometimes, it is useful to simply be able to figure out how many\n"
+ * "arguments are required by a format string. For example, if you\n"
+ * "are extracting a format string for the purpose of generating a\n"
+ * "macro to invoke a printf-like function, you can run the\n"
+ * "formatting string through this function to determine how many\n"
+ * "arguments to provide for in the macro. e.g. for this extraction\n"
+ * "text:\n"
+ * "@example\n\n"
+ * " /" "*=fumble bumble\n"
+ * " * fmt: 'stumble %s: %d\\n'\n"
+ * " =*" "/\n"
+ * "@end example\n\n"
+ * "@noindent\n"
+ * "You may wish to generate a macro:\n"
+ * "@example\n\n"
+ * " #define BUMBLE(a1,a2) printf_like(something,(a1),(a2))\n"
+ * "@end example\n\n"
+ * "@noindent\n"
+ * "You can do this by knowing that the format needs two arguments.\n"
+=*/
+SCM
+ag_scm_format_arg_count(SCM fmt)
+{
+ char* pzFmt = ag_scm2zchars(fmt, WORD_FORMAT);
+ int ct = 0;
+ for (;;) {
+ switch (*(pzFmt++)) {
+ case NUL: goto scanDone;
+ case '%':
+ if (*pzFmt == '%')
+ pzFmt++;
+ else ct++;
+ }
+ } scanDone:;
+
+ return AG_SCM_INT2SCM(ct);
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expPrint.c */
diff --git a/agen5/expState.c b/agen5/expState.c
new file mode 100644
index 0000000..871fbbe
--- /dev/null
+++ b/agen5/expState.c
@@ -0,0 +1,819 @@
+
+/**
+ * @file expState.c
+ *
+ * This module implements expression functions that
+ * query and get state information from AutoGen data.
+ *
+ * Time-stamp: "2012-04-07 09:50:32 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef SCM_HAVE_T_UINT64
+ typedef uint64_t ver_type_t;
+# define VER_UNIT_SHIFT 16ULL
+# if ((SCM_MAJOR_VERSION * 100) + SCM_MINOR_VERSION) >= 108
+# define SCM_FROM(v) scm_from_uint64(v)
+# else
+# define SCM_FROM(v) gh_ulong2scm((unsigned long)v)
+# endif
+
+#else
+ typedef uint32_t ver_type_t;
+# define VER_UNIT_SHIFT 8
+# ifdef HAVE_SCM_FROM_UINT32
+# define SCM_FROM(v) scm_from_uint32(v)
+# else
+# define SCM_FROM(v) gh_ulong2scm((unsigned long)v)
+# endif
+#endif
+
+/* = = = START-STATIC-FORWARD = = = */
+static int
+entry_length(char* name);
+
+static int
+count_entries(char* name);
+
+static SCM
+find_entry_value(SCM op, SCM obj, SCM test);
+
+static ver_type_t
+str2int_ver(char* pz);
+
+static SCM
+do_tpl_file_line(int line_delta, char const * fmt);
+/* = = = END-STATIC-FORWARD = = = */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * EXPRESSION EVALUATION SUPPORT ROUTINES
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int
+entry_length(char* name)
+{
+ def_ent_t** papDefs = find_def_ent_list(name);
+ int res = 0;
+
+ if (papDefs == NULL)
+ return 0;
+
+ for (;;) {
+ def_ent_t* pDE = *(papDefs++);
+ if (pDE == NULL)
+ break;
+ if (pDE->de_type == VALTYP_TEXT)
+ res += strlen(pDE->de_val.dvu_text);
+ else
+ res++;
+ }
+ return res;
+}
+
+
+static int
+count_entries(char* name)
+{
+ def_ent_t** papDefs = find_def_ent_list(name);
+ int res = 0;
+
+ if (papDefs == NULL)
+ return 0;
+
+ for (;;) {
+ def_ent_t* pDE = *(papDefs++);
+ if (pDE == NULL)
+ break;
+ res++;
+ }
+ return res;
+}
+
+/**
+ * Find a definition with a specific value
+ */
+static SCM
+find_entry_value(SCM op, SCM obj, SCM test)
+{
+ bool isIndexed;
+ def_ent_t* pE;
+ char* pzField;
+
+ {
+ char * name = ag_scm2zchars(obj, "find name");
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_FIND_ENT, name);
+
+ pzField = strchr(name, name_sep_ch);
+ if (pzField != NULL)
+ *(pzField++) = NUL;
+
+ pE = find_def_ent(name, &isIndexed);
+ }
+
+ /*
+ * No such entry? return FALSE
+ */
+ if (pE == NULL) {
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fputs(FIND_ENT_FAIL, trace_fp);
+ return SCM_BOOL_F;
+ }
+
+ /*
+ * No subfield? Check the values
+ */
+ if (pzField == NULL) {
+ SCM result;
+ SCM field;
+ if (pE->de_type != VALTYP_TEXT) {
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fputs(FIND_ENT_FAIL, trace_fp);
+ return SCM_BOOL_F; /* Cannot match string -- not a text value */
+ }
+
+ field = AG_SCM_STR02SCM(pE->de_val.dvu_text);
+ result = AG_SCM_APPLY2(op, field, test);
+ if (! isIndexed)
+ while (result == SCM_BOOL_F) {
+
+ pE = pE->de_twin;
+ if (pE == NULL)
+ break;
+
+ field = AG_SCM_STR02SCM(pE->de_val.dvu_text);
+ result = AG_SCM_APPLY2(op, field, test);
+ }
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fputs((result == SCM_BOOL_T) ? FIND_ENT_SUCC : FIND_ENT_FAIL,
+ trace_fp);
+ return result;
+ }
+
+ /*
+ * a subfield for a text macro? return FALSE
+ */
+ if (pE->de_type == VALTYP_TEXT) {
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fputs(FIND_ENT_FAIL, trace_fp);
+ return SCM_BOOL_F;
+ }
+
+ /*
+ * Search the members for what we want.
+ */
+ pzField[-1] = name_sep_ch;
+ {
+ SCM field = AG_SCM_STR02SCM(pzField);
+ SCM result;
+ def_ctx_t ctx = curr_def_ctx;
+
+ curr_def_ctx.dcx_prev = &ctx;
+ curr_def_ctx.dcx_defent = pE->de_val.dvu_entry;
+
+ result = find_entry_value(op, field, test);
+
+ if (! isIndexed)
+ while (result == SCM_BOOL_F) {
+
+ pE = pE->de_twin;
+ if (pE == NULL)
+ break;
+
+ curr_def_ctx.dcx_defent = pE->de_val.dvu_entry;
+ result = find_entry_value(op, field, test);
+ }
+
+ curr_def_ctx = ctx;
+ return result;
+ }
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * EXPRESSION ROUTINES
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc base_name
+ *
+ * what: base output name
+ *
+ * doc: Returns a string containing the base name of the output file(s).
+ * Generally, this is also the base name of the definitions file.
+=*/
+SCM
+ag_scm_base_name(void)
+{
+ return AG_SCM_STR02SCM((char*)(void*)OPT_ARG(BASE_NAME));
+}
+
+/*=gfunc version_compare
+ *
+ * what: compare two version numbers
+ * general_use:
+ *
+ * exparg: op, comparison operator
+ * exparg: v1, first version
+ * exparg: v2, compared-to version
+ *
+ * doc: Converts v1 and v2 strings into 64 bit values and returns the
+ * result of running 'op' on those values. It assumes that the version
+ * is a 1 to 4 part dot-separated series of numbers. Suffixes like,
+ * "5pre4" or "5-pre4" will be interpreted as two numbers. The first
+ * number ("5" in this case) will be decremented and the number after
+ * the "pre" will be added to 0xC000. (Unless your platform is unable
+ * to support 64 bit integer arithmetic. Then it will be added to 0xC0.)
+ * Consequently, these yield true:
+ * @example
+ * (version-compare > "5.8.5" "5.8.5-pre4")
+ * (version-compare > "5.8.5-pre10" "5.8.5-pre4")
+ * @end example
+=*/
+static ver_type_t
+str2int_ver(char* pz)
+{
+ char* pzStr = pz;
+ ver_type_t val = 0;
+ int ix = 4;
+
+ while (--ix >= 0) {
+ unsigned int v;
+ val <<= VER_UNIT_SHIFT;
+ pz = SPN_WHITESPACE_CHARS(pz);
+
+ next_number:
+ if (! IS_DEC_DIGIT_CHAR(*pz)) break;
+ v = (unsigned int)strtoul(pz, &pz, 0) & ((1 << VER_UNIT_SHIFT) - 1);
+ if (pz == NULL)
+ break;
+ val += v;
+ if (*pz == '-') pz++;
+
+ switch (*pz) {
+ case 'p':
+ if ((pz[1] == 'r') && (pz[2] == 'e')) {
+ pz += 3;
+ val = (val << 2) - 1;
+ val <<= (VER_UNIT_SHIFT - 2);
+ if (--ix < 0) goto leave_str2int_ver;
+ goto next_number;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ goto leave_str2int_ver;
+
+ case '.':
+ if (! IS_DEC_DIGIT_CHAR(*(++pz)))
+ goto leave_str2int_ver;
+ break;
+ }
+ } leave_str2int_ver: ;
+
+ while (--ix >= 0) val <<= VER_UNIT_SHIFT;
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_VER_CONVERT, (long long)val, pzStr);
+ return val;
+}
+
+/**
+ * Convert version number strings into a binary representation and compare.
+ */
+SCM
+ag_scm_version_compare(SCM op, SCM v1, SCM v2)
+{
+ ver_type_t val1 = str2int_ver(ag_scm2zchars(v1, "ver"));
+ ver_type_t val2 = str2int_ver(ag_scm2zchars(v2, "ver"));
+ v1 = SCM_FROM(val1);
+ v2 = SCM_FROM(val2);
+ return scm_apply(op, v1, scm_cons(v2, AG_SCM_LISTOFNULL()));
+}
+
+/*=gfunc count
+ *
+ * what: definition count
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc: Count the number of entries for a definition.
+ * The input argument must be a string containing the name
+ * of the AutoGen values to be counted. If there is no
+ * value associated with the name, the result is an SCM
+ * immediate integer value of zero.
+=*/
+SCM
+ag_scm_count(SCM obj)
+{
+ int ent_len = count_entries(ag_scm2zchars(obj, "ag object"));
+
+ return AG_SCM_INT2SCM(ent_len);
+}
+
+
+/*=gfunc def_file
+ *
+ * what: definitions file name
+ *
+ * doc: Get the name of the definitions file.
+ * Returns the name of the source file containing the AutoGen
+ * definitions.
+=*/
+SCM
+ag_scm_def_file(void)
+{
+ return AG_SCM_STR02SCM((char*)(void*)base_ctx->scx_fname);
+}
+
+
+/*=gfunc exist_p
+ *
+ * what: test for value name
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc: return SCM_BOOL_T iff a specified name has an AutoGen value.
+ * The name may include indexes and/or member names.
+ * All but the last member name must be an aggregate definition.
+ * For example:
+ * @example
+ * (exist? "foo[3].bar.baz")
+ * @end example
+ * will yield true if all of the following is true:
+ * @*
+ * There is a member value of either group or string type
+ * named @code{baz} for some group value @code{bar} that
+ * is a member of the @code{foo} group with index @code{3}.
+ * There may be multiple entries of @code{bar} within
+ * @code{foo}, only one needs to contain a value for @code{baz}.
+=*/
+SCM
+ag_scm_exist_p(SCM obj)
+{
+ bool x;
+ SCM res;
+
+ if (find_def_ent(ag_scm2zchars(obj, "ag object"), &x) == NULL)
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ return res;
+}
+
+
+/*=gfunc ag_function_p
+ *
+ * what: test for function
+ *
+ * exparg: ag-name, name of AutoGen macro
+ *
+ * doc: return SCM_BOOL_T if a specified name is a user-defined AutoGen
+ * macro, otherwise return SCM_BOOL_F.
+=*/
+SCM
+ag_scm_ag_function_p(SCM obj)
+{
+ SCM res;
+
+ if (find_tpl(ag_scm2zchars(obj, "ag user macro")) == NULL)
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ return res;
+}
+
+
+/*=gfunc match_value_p
+ *
+ * what: test for matching value
+ *
+ * exparg: op, boolean result operator
+ * exparg: ag-name, name of AutoGen value
+ * exparg: test-str, string to test against
+ *
+ * doc: This function answers the question, "Is there an AutoGen value named
+ * @code{ag-name} with a value that matches the pattern @code{test-str}
+ * using the match function @code{op}?" Return SCM_BOOL_T iff at least
+ * one occurrence of the specified name has such a value. The operator
+ * can be any function that takes two string arguments and yields a
+ * boolean. It is expected that you will use one of the string matching
+ * functions provided by AutoGen.
+ * @*
+ * The value name must follow the same rules as the
+ * @code{ag-name} argument for @code{exist?} (@pxref{SCM exist?}).
+=*/
+SCM
+ag_scm_match_value_p(SCM op, SCM obj, SCM test)
+{
+ if ( (! AG_SCM_IS_PROC(op))
+ || (! AG_SCM_STRING_P(obj)) )
+ return SCM_UNDEFINED;
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_MATCH_VAL, ag_scm2zchars(test, "test val"));
+
+ return find_entry_value(op, obj, test);
+}
+
+
+/*=gfunc get
+ *
+ * what: get named value
+ *
+ * exparg: ag-name, name of AutoGen value
+ * exparg: alt-val, value if not present, optional
+ *
+ * doc:
+ * Get the first string value associated with the name.
+ * It will either return the associated string value (if
+ * the name resolves), the alternate value (if one is provided),
+ * or else the empty string.
+=*/
+SCM
+ag_scm_get(SCM agName, SCM altVal)
+{
+ def_ent_t* pE;
+ bool x;
+
+ pE = (! AG_SCM_STRING_P(agName)) ? NULL :
+ find_def_ent(ag_scm2zchars(agName, "ag value"), &x);
+
+ if ((pE == NULL) || (pE->de_type != VALTYP_TEXT)) {
+ if (AG_SCM_STRING_P(altVal))
+ return altVal;
+ return AG_SCM_STR02SCM(zNil);
+ }
+
+ return AG_SCM_STR02SCM(pE->de_val.dvu_text);
+}
+
+
+/*=gfunc get_c_name
+ *
+ * what: get named value, mapped to C name syntax
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc:
+ *
+ * Get the first string value associated with the name. It will either
+ * return the associated string value (if the name resolves), the alternate
+ * value (if one is provided), or else the empty string. The result is
+ * passed through "string->c-name!".
+=*/
+SCM
+ag_scm_get_c_name(SCM agName)
+{
+ return ag_scm_string_to_c_name_x(
+ ag_scm_get(agName, SCM_UNDEFINED));
+}
+
+
+/*=gfunc get_up_name
+ *
+ * what: get upper cased named value, mapped to C name syntax
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc:
+ *
+ * Get the first string value associated with the name. It will either
+ * return the associated string value (if the name resolves), the alternate
+ * value (if one is provided), or else the empty string. The result is
+ * passed through "string->c-name!" and "string->up-case!".
+=*/
+SCM
+ag_scm_get_up_name(SCM agName)
+{
+ return ag_scm_string_upcase_x(ag_scm_get_c_name(agName));
+}
+
+
+/*=gfunc get_down_name
+ *
+ * what: get lower cased named value, mapped to C name syntax
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc:
+ *
+ * Get the first string value associated with the name. It will either
+ * return the associated string value (if the name resolves), the alternate
+ * value (if one is provided), or else the empty string. The result is
+ * passed through "string->c-name!" and "string->down-case!".
+=*/
+SCM
+ag_scm_get_down_name(SCM agName)
+{
+ return ag_scm_string_downcase_x(ag_scm_get_c_name(agName));
+}
+
+
+/*=gfunc high_lim
+ *
+ * what: get highest value index
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc:
+ *
+ * Returns the highest index associated with an array of definitions.
+ * This is generally, but not necessarily, one less than the
+ * @code{count} value. (The indexes may be specified, rendering a
+ * non-zero based or sparse array of values.)
+ *
+ * This is very useful for specifying the size of a zero-based array
+ * of values where not all values are present. For example:
+ *
+ * @example
+ * tMyStruct myVals[ [+ (+ 1 (high-lim "my-val-list")) +] ];
+ * @end example
+=*/
+SCM
+ag_scm_high_lim(SCM obj)
+{
+ def_ent_t* pE;
+ bool isIndexed;
+
+ pE = find_def_ent(ag_scm2zchars(obj, "ag value"), &isIndexed);
+
+ /*
+ * IF we did not find the entry we are looking for
+ * THEN return zero
+ * ELSE search the twin list for the high entry
+ */
+ if (pE == NULL)
+ return AG_SCM_INT2SCM(0);
+
+ if (isIndexed)
+ return AG_SCM_INT2SCM((int)pE->de_index);
+
+ if (pE->de_etwin != NULL)
+ pE = pE->de_etwin;
+
+ return AG_SCM_INT2SCM((int)pE->de_index);
+}
+
+
+/*=gfunc len
+ *
+ * what: get count of values
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc: If the named object is a group definition, then "len" is
+ * the same as "count". Otherwise, if it is one or more text
+ * definitions, then it is the sum of their string lengths.
+ * If it is a single text definition, then it is equivalent to
+ * @code{(string-length (get "ag-name"))}.
+=*/
+SCM
+ag_scm_len(SCM obj)
+{
+ int len = entry_length(ag_scm2zchars(obj, "ag value"));
+
+ return AG_SCM_INT2SCM(len);
+}
+
+
+/*=gfunc low_lim
+ *
+ * what: get lowest value index
+ *
+ * exparg: ag-name, name of AutoGen value
+ *
+ * doc: Returns the lowest index associated with an array of definitions.
+=*/
+SCM
+ag_scm_low_lim(SCM obj)
+{
+ def_ent_t* pE;
+ bool x;
+
+ pE = find_def_ent(ag_scm2zchars(obj, "ag value"), &x);
+
+ /*
+ * IF we did not find the entry we are looking for
+ * THEN return zero
+ * ELSE we have the low index.
+ */
+ if (pE == NULL)
+ return AG_SCM_INT2SCM(0);
+
+ return AG_SCM_INT2SCM((int)pE->de_index);
+}
+
+
+/*=gfunc set_option
+ *
+ * what: Set a command line option
+ *
+ * exparg: opt, AutoGen option name + its argument
+ *
+ * doc: The text argument must be an option name followed by any needed
+ * option argument. Returns SCM_UNDEFINED.
+=*/
+SCM
+ag_scm_set_option(SCM opt)
+{
+ optionLoadLine(&autogenOptions, ag_scm2zchars(opt, "opt + arg"));
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc suffix
+ *
+ * what: get the current suffix
+ *
+ * doc:
+ * Returns the current active suffix (@pxref{pseudo macro}).
+=*/
+SCM
+ag_scm_suffix(void)
+{
+ return AG_SCM_STR02SCM((char*)curr_sfx);
+}
+
+
+/*=gfunc tpl_file
+ *
+ * what: get the template file name
+ *
+ * exparg: full_path, include full path to file, optonal
+ *
+ * doc: Returns the name of the current template file.
+ * If @code{#t} is passed in as an argument, then the template
+ * file is hunted for in the template search path. Otherwise,
+ * just the unadorned name.
+=*/
+SCM
+ag_scm_tpl_file(SCM full)
+{
+ if (AG_SCM_BOOL_P(full) && AG_SCM_NFALSEP(full)) {
+ static char const * const sfx[] = { TPL_FILE_TPL, NULL };
+
+ char z[AG_PATH_MAX];
+ if (SUCCESSFUL(find_file(tpl_fname, z, sfx, NULL)))
+ return AG_SCM_STR02SCM(z);
+ }
+
+ return AG_SCM_STR02SCM((char*)(void*)tpl_fname);
+}
+
+/**
+ * guts of the template file/line functions
+ */
+static SCM
+do_tpl_file_line(int line_delta, char const * fmt)
+{
+ void * args[2] = {
+ [0] = (void*)current_tpl->td_file,
+ [1] = (void*)((long)cur_macro->md_line + line_delta)
+ };
+ char * buf = strrchr(args[0], DIRCH);
+ if (buf != NULL)
+ args[0] = buf + 1;
+
+ {
+ size_t sz = strlen(fmt) + strlen(args[0]) + 24;
+ buf = ag_scribble(sz);
+ }
+
+ sprintfv(buf, fmt, (snv_constpointer*)args);
+ return AG_SCM_STR02SCM(buf);
+}
+
+/*=gfunc tpl_file_line
+ *
+ * what: get the template file+line number
+ *
+ * exparg: msg-fmt, formatting for line message, optional
+ *
+ * doc:
+ * Returns the file and line number of the current template macro using
+ * either the default format, "from %s line %d", or else the format you
+ * supply. For example, if you want to insert a "C" language file-line
+ * directive, you would supply the format "# %2$d \"%1$s\"", but that
+ * is also already supplied with the scheme variable
+ * @xref{SCM c-file-line-fmt}. You may use it thus:
+ * @example
+ * (tpl-file-line c-file-line-fmt)
+ * @end example
+ *
+ * It is also safe to use the formatting string, "%2$d". AutoGen uses
+ * an argument vector version of printf: @xref{snprintfv},
+ * and it does not need to know the types of each argument in order to
+ * skip forward to the second argument.
+=*/
+SCM
+ag_scm_tpl_file_line(SCM fmt)
+{
+ char const * pzFmt = TPL_FILE_LINE_FMT;
+ if (AG_SCM_STRING_P(fmt))
+ pzFmt = ag_scm2zchars(fmt, "f/l fmt");
+
+ return do_tpl_file_line(0, pzFmt);
+}
+
+/*=gfunc tpl_file_next_line
+ *
+ * what: get the template file plus next line number
+ *
+ * exparg: msg-fmt, formatting for line message, optional
+ *
+ * doc:
+ * This is almost the same as @xref{SCM tpl-file-line}, except that
+ * the line referenced is the next line, per C compiler conventions, and
+ * consequently defaults to the format: # <line-no+1> "<file-name>"
+=*/
+SCM
+ag_scm_tpl_file_next_line(SCM fmt)
+{
+ char const * pzFmt = TPL_FILE_NEXT_LINE_FMT;
+ if (AG_SCM_STRING_P(fmt))
+ pzFmt = ag_scm2zchars(fmt, "f/l fmt");
+
+ return do_tpl_file_line(1, pzFmt);
+}
+
+/*=gfunc def_file_line
+ *
+ * what: get a definition file+line number
+ *
+ * exparg: ag-name, name of AutoGen value
+ * exparg: msg-fmt, formatting for line message, optional
+ *
+ * doc:
+ * Returns the file and line number of a AutoGen defined value, using
+ * either the default format, "from %s line %d", or else the format you
+ * supply. For example, if you want to insert a "C" language file-line
+ * directive, you would supply the format "# %2$d \"%1$s\"", but that
+ * is also already supplied with the scheme variable
+ * @xref{SCM c-file-line-fmt}. You may use it thus:
+ *
+ * @example
+ * (def-file-line "ag-def-name" c-file-line-fmt)
+ * @end example
+ *
+ * It is also safe to use the formatting string, "%2$d". AutoGen uses
+ * an argument vector version of printf: @xref{snprintfv}.
+=*/
+SCM
+ag_scm_def_file_line(SCM obj, SCM fmt)
+{
+ char const * pzFmt = DEF_FILE_LINE_FMT;
+ char * buf;
+ bool x;
+
+ def_ent_t * pE = find_def_ent(ag_scm2zchars(obj, "ag value"), &x);
+
+ /*
+ * IF we did not find the entry we are looking for
+ * THEN return UNDEFINED
+ */
+ if (pE == NULL)
+ return SCM_UNDEFINED;
+
+ if (AG_SCM_STRING_P(fmt))
+ pzFmt = ag_scm2zchars(fmt, "f/l fmt");
+
+ {
+ void * args[2] = {
+ (void*)pE->de_file,
+ (void*)(long)pE->de_line
+ };
+ size_t maxlen;
+
+ buf = strrchr(args[0], DIRCH);
+ if (buf != NULL)
+ args[0] = buf + 1;
+
+ maxlen = strlen(args[0]) + strlen(pzFmt) + LOG10_2to32 + 1;
+ buf = ag_scribble(maxlen);
+ sprintfv(buf, pzFmt, (snv_constpointer*)args);
+ }
+
+ return AG_SCM_STR02SCM(buf);
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expState.c */
diff --git a/agen5/expString.c b/agen5/expString.c
new file mode 100644
index 0000000..f44ffe7
--- /dev/null
+++ b/agen5/expString.c
@@ -0,0 +1,1132 @@
+
+/**
+ * @file expString.c
+ *
+ * Time-stamp: "2012-03-31 13:33:55 bkorb"
+ *
+ * This module implements expression functions that
+ * manipulate string values.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static size_t
+string_size(char const * pzScn, size_t newLineSize);
+
+static SCM
+makeString(char const * pzText, char const * pzNewLine, size_t newLineSize);
+
+static size_t
+stringify_for_sh(char * pzNew, uint_t qt, char const * pzDta);
+
+static SCM
+shell_stringify(SCM obj, uint_t qt);
+
+static int
+sub_count(char const * haystack, char const * needle);
+
+static void
+do_substitution(
+ char const * src_str,
+ ssize_t str_len,
+ SCM match,
+ SCM repl,
+ char ** ppz_res,
+ ssize_t * res_len);
+/* = = = END-STATIC-FORWARD = = = */
+
+static size_t
+string_size(char const * pzScn, size_t newLineSize)
+{
+ /*
+ * Start by counting the start and end quotes, plus the NUL.
+ */
+ size_t dtaSize = 3;
+
+ for (;;) {
+ char ch = *(pzScn++);
+ if ((ch >= ' ') && (ch <= '~')) {
+
+ /*
+ * One for each character, plus a backquote when needed
+ */
+ dtaSize++;
+ if ((ch == '"') || (ch == '\\'))
+ dtaSize++;
+ }
+
+ /*
+ * When not a normal character, then count the characters
+ * required to represent whatever it is.
+ */
+ else switch (ch) {
+ case NUL:
+ return dtaSize;
+
+ case NL:
+ dtaSize += newLineSize;
+ break;
+
+ case TAB:
+ case '\a':
+ case '\b':
+ case '\f':
+ case '\r':
+ case '\v':
+ dtaSize += 2;
+ break;
+
+ default:
+ dtaSize += 4;
+ }
+ }
+}
+
+static SCM
+makeString(char const * pzText, char const * pzNewLine, size_t newLineSize)
+{
+ char z[SCRIBBLE_SIZE];
+ char* pzDta;
+ char* pzFre;
+ char const * pzScn = pzText;
+ size_t dtaSize = string_size(pzText, newLineSize);
+
+ /*
+ * We now know how big the string is going to be.
+ * Allocate what we need.
+ */
+ if (dtaSize >= sizeof(z))
+ pzFre = pzDta = AGALOC(dtaSize, "quoting string");
+ else pzFre = pzDta = z;
+
+ *(pzDta++) = '"';
+
+ for (;;) {
+ unsigned char ch = (unsigned char)*pzScn;
+ if ((ch >= ' ') && (ch <= '~')) {
+ if ((ch == '"') || (ch == '\\'))
+ /*
+ * We must escape these characters in the output string
+ */
+ *(pzDta++) = '\\';
+ *(pzDta++) = ch;
+
+ } else switch (ch) {
+ case NUL:
+ goto copyDone;
+
+ case NL:
+ /*
+ * place contiguous new-lines on a single line
+ */
+ while (pzScn[1] == NL) {
+ *(pzDta++) = '\\';
+ *(pzDta++) = 'n';
+ pzScn++;
+ }
+
+ /*
+ * Replace the new-line with its escaped representation.
+ * Also, break and restart the output string, indented
+ * 7 spaces (so that after the '"' char is printed,
+ * any contained tabbing will look correct).
+ * Do *not* start a new line if there are no more data.
+ */
+ if (pzScn[1] == NUL) {
+ *(pzDta++) = '\\';
+ *(pzDta++) = 'n';
+ goto copyDone;
+ }
+
+ strcpy(pzDta, pzNewLine);
+ pzDta += newLineSize;
+ break;
+
+ case '\a':
+ *(pzDta++) = '\\';
+ *(pzDta++) = 'a';
+ break;
+
+ case '\b':
+ *(pzDta++) = '\\';
+ *(pzDta++) = 'b';
+ break;
+
+ case '\f':
+ *(pzDta++) = '\\';
+ *(pzDta++) = 'f';
+ break;
+
+ case '\r':
+ *(pzDta++) = '\\';
+ *(pzDta++) = 'r';
+ break;
+
+ case TAB:
+ *(pzDta++) = '\\';
+ *(pzDta++) = 't';
+ break;
+
+ case '\v':
+ *(pzDta++) = '\\';
+ *(pzDta++) = 'v';
+ break;
+
+ default:
+ /*
+ * sprintf is safe here, because we already computed
+ * the amount of space we will be using.
+ */
+ sprintf(pzDta, MK_STR_OCT_FMT, ch);
+ pzDta += 4;
+ }
+
+ pzScn++;
+ } copyDone:
+
+ /*
+ * End of string. Terminate the quoted output.
+ * If necessary, deallocate the text string.
+ * Return the scan resumption point.
+ */
+ *(pzDta++) = '"';
+ *pzDta = NUL;
+
+ {
+ SCM res = AG_SCM_STR02SCM(pzFre);
+ if (pzFre != z)
+ AGFREE(pzFre);
+ return res;
+ }
+}
+
+static size_t
+stringify_for_sh(char * pzNew, uint_t qt, char const * pzDta)
+{
+ char * pz = pzNew;
+ *(pz++) = qt;
+
+ for (;;) {
+ char c = (*(pz++) = *(pzDta++));
+ switch (c) {
+ case NUL:
+ pz[-1] = qt;
+ *pz = NUL;
+
+ return (pz - pzNew);
+
+ case '\\':
+ /*
+ * If someone went to the trouble to escape a backquote or a
+ * dollar sign, then we should not neutralize it. Note that
+ * we handle a following backslash as a normal character.
+ *
+ * i.e. \\ --> \\\\ *BUT* \\$ --> \\\$
+ */
+ c = *pzDta;
+ switch (*pzDta) {
+ case '$':
+ break;
+
+ case '"':
+ case '`':
+ /*
+ * IF the ensuing quote character does *NOT* match the
+ * quote character for the string, then we will preserve
+ * the single copy of the backslash. If it does match,
+ * then we will double the backslash and a third backslash
+ * will be inserted when we emit the quote character.
+ */
+ if ((unsigned)c != qt)
+ break;
+ /* FALLTHROUGH */
+
+ default:
+ *(pz++) = '\\'; /* \ --> \\ */
+ }
+ break;
+
+ case '"': case '`':
+ if ((unsigned)c == qt) {
+ /*
+ * This routine does both `xx` and "xx" strings, we have
+ * to worry about this stuff differently. I.e., in ""
+ * strings, add a single \ in front of ", and in ``
+ * preserve a add \ in front of `.
+ */
+ pz[-1] = '\\'; /* " --> \" */
+ *(pz++) = c;
+ }
+ }
+ }
+}
+
+static SCM
+shell_stringify(SCM obj, uint_t qt)
+{
+ char* pzNew;
+ size_t dtaSize = 3;
+ char* pzDta = ag_scm2zchars(obj, "AG Object");
+ char* pz = pzDta;
+
+ for (;;) {
+ char c = *(pz++);
+
+ switch (c) {
+ case NUL:
+ goto loopDone1;
+
+ case '"': case '`': case '\\':
+ dtaSize += 2;
+ break;
+
+ default:
+ dtaSize++;
+ }
+ } loopDone1:;
+
+ pzNew = AGALOC(dtaSize, "shell string");
+ dtaSize = stringify_for_sh(pzNew, qt, pzDta);
+
+ {
+ SCM res = AG_SCM_STR2SCM(pzNew, dtaSize);
+ AGFREE(pzNew);
+ return res;
+ }
+}
+
+static int
+sub_count(char const * haystack, char const * needle)
+{
+ int repCt = 0;
+ size_t needle_len = strlen(needle);
+
+ for (;;) {
+ haystack = strstr(haystack, needle);
+ if (haystack == NULL) break;
+ repCt++;
+ haystack += needle_len;
+ }
+ return repCt;
+}
+
+/**
+ * Replace marker text.
+ *
+ * Replace all occurrances of the marker text with the substitution text.
+ * The result is stored in an automatically freed temporary buffer.
+ *
+ * @param src_str The source string
+ * @param str_len The length of the string
+ * @param match the SCM-ized marker string
+ * @param repl the SCM-ized replacement string
+ * @param ppz_res pointer to the result pointer
+ * @param res_len pointer to result length
+ */
+static void
+do_substitution(
+ char const * src_str,
+ ssize_t str_len,
+ SCM match,
+ SCM repl,
+ char ** ppz_res,
+ ssize_t * res_len)
+{
+ char* pzMatch = ag_scm2zchars(match, "match text");
+ char* rep_str = ag_scm2zchars(repl, "repl text");
+ int mark_len = AG_SCM_STRLEN(match);
+ int repl_len = AG_SCM_STRLEN(repl);
+
+ {
+ int ct = sub_count(src_str, pzMatch);
+ if (ct == 0)
+ return; /* No substitutions -- no work. */
+
+ str_len += (repl_len - mark_len) * ct;
+ }
+
+ {
+ char * dest = ag_scribble(str_len + 1);
+ *ppz_res = dest;
+ *res_len = str_len;
+
+ for (;;) {
+ char const * next = strstr(src_str, pzMatch);
+ size_t len;
+
+ if (next == NULL)
+ break;
+ len = next - src_str;
+ if (len != 0) {
+ memcpy(dest, src_str, len);
+ dest += len;
+ }
+ memcpy(dest, rep_str, (size_t)repl_len);
+ dest += repl_len;
+ src_str = next + mark_len;
+ }
+
+ strcpy(dest, src_str);
+ }
+}
+
+
+/*
+ * Recursive routine. It calls itself for list values and calls
+ * "do_substitution" for string values. Each substitution will
+ * be done in the order found in the tree walk of list values.
+ * The "match" and "repl" trees *must* be identical in structure.
+ */
+LOCAL void
+do_multi_subs(char ** ppzStr, ssize_t * pStrLen, SCM match, SCM repl)
+{
+ char* pzStr = *ppzStr;
+ char* pzNxt = pzStr;
+
+ /*
+ * Loop for as long as our list has more entries
+ */
+ while (! AG_SCM_NULLP(match)) {
+ /*
+ * "CAR" is the current value, "CDR" is rest of list
+ */
+ SCM matchCar = SCM_CAR(match);
+ SCM replCar = SCM_CAR(repl);
+
+ match = SCM_CDR(match);
+ repl = SCM_CDR(repl);
+
+ if (AG_SCM_STRING_P(matchCar)) {
+ do_substitution(pzStr, *pStrLen, matchCar, replCar,
+ &pzNxt, pStrLen);
+
+ // coverity[use_after_free] -- invalid alias analysis
+ pzStr = pzNxt;
+ }
+
+ else if (AG_SCM_LIST_P(matchCar))
+ do_multi_subs(&pzStr, pStrLen, matchCar, replCar);
+
+ else
+ /*
+ * Whatever it is it is not part of what we would expect. Bail.
+ */
+ break;
+ }
+
+ *ppzStr = pzStr;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * EXPRESSION EVALUATION ROUTINES
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc in_p
+ *
+ * what: test for string in list
+ * general_use:
+ * exparg: test-string, string to look for
+ * exparg: string-list, list of strings to check,, list
+ *
+ * doc: Return SCM_BOOL_T if the first argument string is found
+ * in one of the entries in the second (list-of-strings) argument.
+=*/
+SCM
+ag_scm_in_p(SCM obj, SCM list)
+{
+ int len;
+ size_t lenz;
+ SCM car;
+ char const * pz1;
+
+ if (! AG_SCM_STRING_P(obj))
+ return SCM_UNDEFINED;
+
+ pz1 = AG_SCM_CHARS(obj);
+ lenz = AG_SCM_STRLEN(obj);
+
+ /*
+ * If the second argument is a string somehow, then treat
+ * this as a straight out string comparison
+ */
+ if (AG_SCM_STRING_P(list)) {
+ if ( (AG_SCM_STRLEN(list) == lenz)
+ && (strncmp(pz1, AG_SCM_CHARS(list), lenz) == 0))
+ return SCM_BOOL_T;
+ return SCM_BOOL_F;
+ }
+
+ len = scm_ilength(list);
+ if (len == 0)
+ return SCM_BOOL_F;
+
+ /*
+ * Search all the lists and sub-lists passed in
+ */
+ while (len-- > 0) {
+ car = SCM_CAR(list);
+ list = SCM_CDR(list);
+
+ /*
+ * This routine is listed as getting a list as the second
+ * argument. That means that if someone builds a list and
+ * hands it to us, it magically becomes a nested list.
+ * This unravels that.
+ */
+ if (! AG_SCM_STRING_P(car)) {
+ if (ag_scm_in_p(obj, car) == SCM_BOOL_T)
+ return SCM_BOOL_T;
+ continue;
+ }
+
+ if ( (AG_SCM_STRLEN(car) == lenz)
+ && (strncmp(pz1, AG_SCM_CHARS(car), lenz) == 0) )
+ return SCM_BOOL_T;
+ }
+
+ return SCM_BOOL_F;
+}
+
+
+/*=gfunc join
+ *
+ * what: join string list with separator
+ * general_use:
+ * exparg: separator, string to insert between entries
+ * exparg: list, list of strings to join,, list
+ *
+ * doc: With the first argument as the separator string,
+ * joins together an a-list of strings into one long string.
+ * The list may contain nested lists, partly because you
+ * cannot always control that.
+=*/
+SCM
+ag_scm_join(SCM sep, SCM list)
+{
+ int l_len, sv_l_len;
+ SCM car;
+ SCM alist = list;
+ size_t sep_len;
+ size_t str_len;
+ char * pzRes;
+ char const * pzSep;
+ char * pzScan;
+
+ if (! AG_SCM_STRING_P(sep))
+ return SCM_UNDEFINED;
+
+ sv_l_len = l_len = scm_ilength(list);
+ if (l_len == 0)
+ return AG_SCM_STR02SCM(zNil);
+
+ pzSep = AG_SCM_CHARS(sep);
+ sep_len = AG_SCM_STRLEN(sep);
+ str_len = 0;
+
+ /*
+ * Count up the lengths of all the strings to be joined.
+ */
+ for (;;) {
+ car = SCM_CAR(list);
+ list = SCM_CDR(list);
+
+ /*
+ * This routine is listed as getting a list as the second
+ * argument. That means that if someone builds a list and
+ * hands it to us, it magically becomes a nested list.
+ * This unravels that.
+ */
+ if (! AG_SCM_STRING_P(car)) {
+ if (car != SCM_UNDEFINED)
+ car = ag_scm_join(sep, car);
+ if (! AG_SCM_STRING_P(car))
+ return SCM_UNDEFINED;
+ }
+
+ str_len += AG_SCM_STRLEN(car);
+
+ if (--l_len <= 0)
+ break;
+
+ str_len += sep_len;
+ }
+
+ l_len = sv_l_len;
+ pzRes = pzScan = ag_scribble(str_len);
+
+ /*
+ * Now, copy each one into the output
+ */
+ for (;;) {
+ size_t cpy_len;
+
+ car = SCM_CAR(alist);
+ alist = SCM_CDR(alist);
+
+ /*
+ * This unravels nested lists.
+ */
+ if (! AG_SCM_STRING_P(car))
+ car = ag_scm_join(sep, car);
+
+ cpy_len = AG_SCM_STRLEN(car);
+ memcpy((void*)pzScan, AG_SCM_CHARS(car), cpy_len);
+ pzScan += cpy_len;
+
+ /*
+ * IF we reach zero, then do not insert a separation and bail out
+ */
+ if (--l_len <= 0)
+ break;
+ memcpy((void*)pzScan, (void*)pzSep, sep_len);
+ pzScan += sep_len;
+ }
+
+ return AG_SCM_STR2SCM(pzRes, str_len);
+}
+
+
+/*=gfunc prefix
+ *
+ * what: prefix lines with a string
+ * general_use:
+ *
+ * exparg: prefix, string to insert at start of each line
+ * exparg: text, multi-line block of text
+ *
+ * doc:
+ * Prefix every line in the second string with the first string.
+ *
+ * For example, if the first string is "# " and the second contains:
+ * @example
+ * two
+ * lines
+ * @end example
+ * @noindent
+ * The result string will contain:
+ * @example
+ * # two
+ * # lines
+ * @end example
+=*/
+SCM
+ag_scm_prefix(SCM prefix, SCM text)
+{
+ char* pzPfx;
+ char* pzText;
+ char* pzDta;
+ size_t out_size, rem_size;
+ size_t pfx_size;
+ char* pzRes;
+
+ pzPfx = ag_scm2zchars(prefix, "prefix");
+ pzDta = \
+ pzText = ag_scm2zchars(text, "text");
+ pfx_size = strlen(pzPfx);
+ out_size = pfx_size;
+
+ for (;;) {
+ switch (*(pzText++)) {
+ case NUL:
+ goto exit_count;
+ case NL:
+ out_size += pfx_size;
+ /* FALLTHROUGH */
+ default:
+ out_size++;
+ }
+ } exit_count:;
+
+ pzText = pzDta;
+ pzRes = pzDta = ag_scribble(rem_size = out_size);
+ strcpy(pzDta, pzPfx);
+ pzDta += pfx_size;
+ rem_size -= pfx_size;
+ pfx_size++;
+
+ for (;;) {
+ char ch = *(pzText++);
+ switch (ch) {
+ case NUL:
+ if (rem_size != 0)
+ AG_ABEND(PREFIX_FAIL);
+
+ return AG_SCM_STR2SCM(pzRes, out_size);
+
+ case NL:
+ *pzDta = ch;
+ strcpy(pzDta+1, pzPfx);
+ pzDta += pfx_size;
+ rem_size -= pfx_size;
+ break;
+
+ default:
+ rem_size--;
+ *(pzDta++) = ch;
+ break;
+ }
+ }
+}
+
+/*=gfunc raw_shell_str
+ *
+ * what: single quote shell string
+ * general_use:
+ *
+ * exparg: string, string to transform
+ *
+ * doc:
+ * Convert the text of the string into a singly quoted string
+ * that a normal shell will process into the original string.
+ * (It will not do macro expansion later, either.)
+ * Contained single quotes become tripled, with the middle quote
+ * escaped with a backslash. Normal shells will reconstitute the
+ * original string.
+ *
+ * @strong{Notice}: some shells will not correctly handle unusual
+ * non-printing characters. This routine works for most reasonably
+ * conventional ASCII strings.
+=*/
+SCM
+ag_scm_raw_shell_str(SCM obj)
+{
+ char* pzDta;
+ char* pz;
+ char* pzFree;
+
+ pzDta = ag_scm2zchars(obj, "AG Object");
+
+ {
+ size_t dtaSize = AG_SCM_STRLEN(obj) + 3; /* NUL + 2 quotes */
+ pz = pzDta-1;
+ for (;;) {
+ pz = strchr(pz+1, '\'');
+ if (pz == NULL)
+ break;
+ dtaSize += 3; /* '\'' -> 3 additional chars */
+ }
+
+ pzFree = pz = AGALOC(dtaSize + 2, "raw string");
+ }
+
+ /*
+ * Handle leading single quotes before starting the first quote.
+ */
+ while (*pzDta == '\'') {
+ *(pz++) = '\\';
+ *(pz++) = '\'';
+
+ /*
+ * IF pure single quotes, then we're done.
+ */
+ if (*++pzDta == NUL) {
+ *pz = NUL;
+ goto returnString;
+ }
+ }
+
+ /*
+ * Start quoting. If the string is empty, we wind up with two quotes.
+ */
+ *(pz++) = '\'';
+
+ for (;;) {
+ switch (*(pz++) = *(pzDta++)) {
+ case NUL:
+ goto loopDone;
+
+ case '\'':
+ /*
+ * We've inserted a single quote, which ends the quoting session.
+ * Now, insert escaped quotes for every quote char we find, then
+ * restart the quoting.
+ */
+ pzDta--;
+ do {
+ *(pz++) = '\\';
+ *(pz++) = '\'';
+ } while (*++pzDta == '\'');
+ if (*pzDta == NUL) {
+ *pz = NUL;
+ goto returnString;
+ }
+ *(pz++) = '\'';
+ }
+ } loopDone:;
+ pz[-1] = '\'';
+ *pz = NUL;
+
+ returnString:
+ {
+ SCM res = AG_SCM_STR02SCM(pzFree);
+ AGFREE(pzFree);
+ return res;
+ }
+}
+
+
+/*=gfunc shell_str
+ *
+ * what: double quote shell string
+ * general_use:
+ *
+ * exparg: string, string to transform
+ *
+ * doc:
+ *
+ * Convert the text of the string into a double quoted string that a normal
+ * shell will process into the original string, almost. It will add the
+ * escape character @code{\\} before two special characters to
+ * accomplish this: the backslash @code{\\} and double quote @code{"}.
+ *
+ * @strong{Notice}: some shells will not correctly handle unusual
+ * non-printing characters. This routine works for most reasonably
+ * conventional ASCII strings.
+ *
+ * @strong{WARNING}:
+ *@*
+ * This function omits the extra backslash in front of a backslash, however,
+ * if it is followed by either a backquote or a dollar sign. It must do this
+ * because otherwise it would be impossible to protect the dollar sign or
+ * backquote from shell evaluation. Consequently, it is not possible to
+ * render the strings "\\$" or "\\`". The lesser of two evils.
+ *
+ * All others characters are copied directly into the output.
+ *
+ * The @code{sub-shell-str} variation of this routine behaves identically,
+ * except that the extra backslash is omitted in front of @code{"} instead
+ * of @code{`}. You have to think about it. I'm open to suggestions.
+ *
+ * Meanwhile, the best way to document is with a detailed output example.
+ * If the backslashes make it through the text processing correctly,
+ * below you will see what happens with three example strings. The first
+ * example string contains a list of quoted @code{foo}s, the second is
+ * the same with a single backslash before the quote characters and the
+ * last is with two backslash escapes. Below each is the result of the
+ * @code{raw-shell-str}, @code{shell-str} and @code{sub-shell-str} functions.
+ *
+ * @example
+ * foo[0] ''foo'' 'foo' "foo" `foo` $foo
+ * raw-shell-str -> \'\''foo'\'\'' '\''foo'\'' "foo" `foo` $foo'
+ * shell-str -> "''foo'' 'foo' \"foo\" `foo` $foo"
+ * sub-shell-str -> `''foo'' 'foo' "foo" \`foo\` $foo`
+ *
+ * foo[1] \'bar\' \"bar\" \`bar\` \$bar
+ * raw-shell-str -> '\'\''bar\'\'' \"bar\" \`bar\` \$bar'
+ * shell-str -> "\\'bar\\' \\\"bar\\\" \`bar\` \$bar"
+ * sub-shell-str -> `\\'bar\\' \"bar\" \\\`bar\\\` \$bar`
+ *
+ * foo[2] \\'BAZ\\' \\"BAZ\\" \\`BAZ\\` \\$BAZ
+ * raw-shell-str -> '\\'\''BAZ\\'\'' \\"BAZ\\" \\`BAZ\\` \\$BAZ'
+ * shell-str -> "\\\\'BAZ\\\\' \\\\\"BAZ\\\\\" \\\`BAZ\\\` \\\$BAZ"
+ * sub-shell-str -> `\\\\'BAZ\\\\' \\\"BAZ\\\" \\\\\`BAZ\\\\\` \\\$BAZ`
+ * @end example
+ *
+ * There should be four, three, five and three backslashes for the four
+ * examples on the last line, respectively. The next to last line should
+ * have four, five, three and three backslashes. If this was not accurately
+ * reproduced, take a look at the agen5/test/shell.test test. Notice the
+ * backslashes in front of the dollar signs. It goes from zero to one to
+ * three for the "cooked" string examples.
+=*/
+SCM
+ag_scm_shell_str(SCM obj)
+{
+ return shell_stringify(obj, (unsigned)'"');
+}
+
+/*=gfunc sub_shell_str
+ *
+ * what: back quoted (sub-)shell string
+ * general_use:
+ *
+ * exparg: string, string to transform
+ *
+ * doc:
+ * This function is substantially identical to @code{shell-str}, except
+ * that the quoting character is @code{`} and the "leave the escape alone"
+ * character is @code{"}.
+=*/
+SCM
+ag_scm_sub_shell_str(SCM obj)
+{
+ return shell_stringify(obj, (unsigned)'`');
+}
+
+
+/*=gfunc stack
+ *
+ * what: make list of AutoGen values
+ *
+ * exparg: ag-name, AutoGen value name
+ *
+ * doc: Create a scheme list of all the strings that are associated
+ * with a name. They must all be text values or we choke.
+=*/
+SCM
+ag_scm_stack(SCM obj)
+{
+ SCM res;
+ SCM * pos = &res;
+ def_ent_t** ppDE;
+ def_ent_t* pDE;
+ SCM str;
+
+ res = SCM_EOL;
+
+ ppDE = find_def_ent_list(ag_scm2zchars(obj, "AG Object"));
+ if (ppDE == NULL)
+ return SCM_EOL;
+
+ for (;;) {
+ pDE = *(ppDE++);
+
+ if (pDE == NULL)
+ break;
+
+ if (pDE->de_type != VALTYP_TEXT)
+ return SCM_UNDEFINED;
+
+ str = AG_SCM_STR02SCM(pDE->de_val.dvu_text);
+ *pos = scm_cons(str, SCM_EOL);
+ pos = SCM_CDRLOC(*pos);
+ }
+
+ return res;
+}
+
+
+/*=gfunc kr_string
+ *
+ * what: emit string for K&R C
+ * general_use:
+ *
+ * exparg: string, string to reformat
+ *
+ * doc:
+ * Reform a string so that, when printed, a K&R C compiler will be able
+ * to compile the data and construct a string that contains exactly
+ * what the current string contains. Many non-printing characters are
+ * replaced with escape sequences. New-lines are replaced with a
+ * backslash-n-backslash and newline sequence,
+=*/
+SCM
+ag_scm_kr_string(SCM str)
+{
+ return makeString(ag_scm2zchars(str, "str"),
+ KR_STRING_NEWLINE, KR_STRING_NEWLINE_LEN);
+}
+
+
+/*=gfunc c_string
+ *
+ * what: emit string for ANSI C
+ * general_use:
+ *
+ * exparg: string, string to reformat
+ *
+ * doc:
+ * Reform a string so that, when printed, the C compiler will be able to
+ * compile the data and construct a string that contains exactly what the
+ * current string contains. Many non-printing characters are replaced with
+ * escape sequences. Newlines are replaced with a backslash, an @code{n}, a
+ * closing quote, a newline, seven spaces and another re-opening quote. The
+ * compiler will implicitly concatenate them. The reader will see line
+ * breaks.
+ *
+ * A K&R compiler will choke. Use @code{kr-string} for that compiler.
+ *
+=*/
+SCM
+ag_scm_c_string(SCM str)
+{
+ return makeString(ag_scm2zchars(str, "str"),
+ C_STRING_NEWLINE, C_STRING_NEWLINE_LEN);
+}
+
+
+/*=gfunc string_tr_x
+ *
+ * what: convert characters
+ * general_use:
+ *
+ * exparg: source, string to transform
+ * exparg: match, characters to be converted
+ * exparg: translation, conversion list
+ *
+ * doc: This is the same as the @code{tr(1)} program, except the
+ * string to transform is the first argument. The second and
+ * third arguments are used to construct mapping arrays for the
+ * transformation of the first argument.
+ *
+ * It is too bad this little program has so many different
+ * and incompatible implementations!
+=*/
+SCM
+ag_scm_string_tr_x(SCM str, SCM from_xform, SCM to_xform)
+{
+ unsigned char ch_map[ 1 << 8 /* bits-per-byte */ ];
+ int i = sizeof(ch_map) - 1;
+ char* pzFrom = ag_scm2zchars(from_xform, "str");
+ char* pzTo = ag_scm2zchars(to_xform, "str");
+
+ do {
+ ch_map[i] = i;
+ } while (--i > 0);
+
+ for (; i <= (int)sizeof(ch_map) - 1; i++) {
+ unsigned char fch = (unsigned char)*(pzFrom++);
+ unsigned char tch = (unsigned char)*(pzTo++);
+
+ if (tch == NUL) {
+ pzTo--;
+ tch = pzTo[-1];
+ }
+
+ switch (fch) {
+ case NUL:
+ goto mapDone;
+
+ case '-':
+ if ((i > 0) && (tch == '-')) {
+ unsigned char fs = (unsigned char)pzFrom[-2];
+ unsigned char fe = (unsigned char)pzFrom[0];
+ unsigned char ts = (unsigned char)pzTo[-2];
+ unsigned char te = (unsigned char)pzTo[0];
+ if (te != NUL) {
+ while (fs < fe) {
+ ch_map[ fs++ ] = ts;
+ if (ts < te) ts++;
+ }
+ break;
+ }
+ }
+
+ default:
+ ch_map[ fch ] = tch;
+ }
+ } mapDone:;
+
+ pzTo = (char*)(void*)AG_SCM_CHARS(str);
+ i = AG_SCM_STRLEN(str);
+ while (i-- > 0) {
+ *pzTo = ch_map[ (int)*pzTo ];
+ pzTo++;
+ }
+ return str;
+}
+
+/*=gfunc string_tr
+ *
+ * what: convert characters with new result
+ * general_use:
+ *
+ * exparg: source, string to transform
+ * exparg: match, characters to be converted
+ * exparg: translation, conversion list
+ *
+ * doc: This is identical to @code{string-tr!}, except that it does not
+ * over-write the previous value.
+=*/
+SCM
+ag_scm_string_tr(SCM Str, SCM From, SCM To)
+{
+ size_t lenz = AG_SCM_STRLEN(Str);
+ SCM res = AG_SCM_STR2SCM(AG_SCM_CHARS(Str), lenz);
+ return ag_scm_string_tr_x(res, From, To);
+}
+
+/*=gfunc string_substitute
+ *
+ * what: multiple global replacements
+ * general_use:
+ *
+ * exparg: source, string to transform
+ * exparg: match, substring or substring list to be replaced
+ * exparg: repl, replacement strings or substrings
+ *
+ * doc: @code{match} and @code{repl} may be either a single string or
+ * a list of strings. Either way, they must have the same structure
+ * and number of elements. For example, to replace all amphersands,
+ * less than and greater than characters, do something like this:
+ *
+ * @example
+ * (string-substitute source
+ * (list "&" "<" ">")
+ * (list "&amp;" "&lt;" "&gt;"))
+ * @end example
+=*/
+SCM
+ag_scm_string_substitute(SCM Str, SCM Match, SCM Repl)
+{
+ char const * pzStr;
+ ssize_t len;
+ SCM res;
+
+ if (! AG_SCM_STRING_P(Str))
+ return SCM_UNDEFINED;
+
+ pzStr = AG_SCM_CHARS(Str);
+ len = AG_SCM_STRLEN(Str);
+
+ if (AG_SCM_STRING_P(Match))
+ do_substitution(pzStr, len, Match, Repl, (char**)&pzStr, &len);
+ else
+ do_multi_subs((char**)&pzStr, &len, Match, Repl);
+
+ res = AG_SCM_STR2SCM(pzStr, len);
+ return res;
+}
+
+/*=gfunc time_string_to_number
+ *
+ * what: duration string to seconds
+ * general_use:
+ * exparg: time_spec, string to parse
+ *
+ * doc: Convert the argument string to a time period in seconds.
+ * The string may use multiple parts consisting of days, hours
+ * minutes and seconds. These are indicated with a suffix of
+ * @code{d}, @code{h}, @code{m} and @code{s} respectively.
+ * Hours, minutes and seconds may also be represented with
+ * @code{HH:MM:SS} or, without hours, as @code{MM:SS}.
+=*/
+SCM
+ag_scm_time_string_to_number(SCM time_spec)
+{
+ extern time_t parse_duration(char const * in_pz);
+
+ char const * pz;
+ time_t time_period;
+
+ if (! AG_SCM_STRING_P(time_spec))
+ return SCM_UNDEFINED;
+
+ pz = AG_SCM_CHARS(time_spec);
+ time_period = parse_duration(pz);
+
+ return AG_SCM_INT2SCM((int)time_period);
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expString.c */
diff --git a/agen5/expr.h b/agen5/expr.h
new file mode 100644
index 0000000..c181729
--- /dev/null
+++ b/agen5/expr.h
@@ -0,0 +1,160 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (expr.h)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:37 AM by AutoGen 5.16.2pre7
+ * From the definitions expr.def
+ * and the template file snarf
+ *
+ * copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Guile Implementation Routines - for the ag group
+ */
+#ifndef GUILE_PROCS_EXPR_H_GUARD
+#define GUILE_PROCS_EXPR_H_GUARD 1
+#if GUILE_VERSION >= 108000
+# include <libguile.h>
+#else
+# include <guile/gh.h>
+#endif
+
+typedef enum {
+ GH_TYPE_UNDEFINED = 0,
+ GH_TYPE_BOOLEAN,
+ GH_TYPE_SYMBOL,
+ GH_TYPE_CHAR,
+ GH_TYPE_VECTOR,
+ GH_TYPE_PAIR,
+ GH_TYPE_NUMBER,
+ GH_TYPE_STRING,
+ GH_TYPE_PROCEDURE,
+ GH_TYPE_LIST,
+ GH_TYPE_INEXACT,
+ GH_TYPE_EXACT
+} teGuileType;
+
+extern SCM ag_scm_ag_fprintf(SCM, SCM, SCM);
+extern SCM ag_scm_ag_function_p(SCM);
+extern SCM ag_scm_agpl(SCM, SCM);
+extern SCM ag_scm_base_name(void);
+extern SCM ag_scm_bsd(SCM, SCM, SCM);
+extern SCM ag_scm_c_string(SCM);
+extern SCM ag_scm_chdir(SCM);
+extern SCM ag_scm_count(SCM);
+extern SCM ag_scm_def_file(void);
+extern SCM ag_scm_def_file_line(SCM, SCM);
+extern SCM ag_scm_dne(SCM, SCM, SCM);
+extern SCM ag_scm_emit(SCM);
+extern SCM ag_scm_error(SCM);
+extern SCM ag_scm_error_source_line(void);
+extern SCM ag_scm_exist_p(SCM);
+extern SCM ag_scm_extract(SCM, SCM, SCM, SCM);
+extern SCM ag_scm_find_file(SCM, SCM);
+extern SCM ag_scm_first_for_p(SCM);
+extern SCM ag_scm_for_by(SCM);
+extern SCM ag_scm_for_from(SCM);
+extern SCM ag_scm_for_index(SCM);
+extern SCM ag_scm_for_sep(SCM);
+extern SCM ag_scm_for_to(SCM);
+extern SCM ag_scm_format_arg_count(SCM);
+extern SCM ag_scm_fprintf(SCM, SCM, SCM);
+extern SCM ag_scm_get(SCM, SCM);
+extern SCM ag_scm_get_c_name(SCM);
+extern SCM ag_scm_get_down_name(SCM);
+extern SCM ag_scm_get_up_name(SCM);
+extern SCM ag_scm_gperf(SCM, SCM);
+extern SCM ag_scm_gpl(SCM, SCM);
+extern SCM ag_scm_hide_email(SCM, SCM);
+extern SCM ag_scm_high_lim(SCM);
+extern SCM ag_scm_in_p(SCM, SCM);
+extern SCM ag_scm_join(SCM, SCM);
+extern SCM ag_scm_kr_string(SCM);
+extern SCM ag_scm_last_for_p(SCM);
+extern SCM ag_scm_len(SCM);
+extern SCM ag_scm_lgpl(SCM, SCM, SCM);
+extern SCM ag_scm_license(SCM, SCM, SCM, SCM);
+extern SCM ag_scm_license_description(SCM, SCM, SCM, SCM);
+extern SCM ag_scm_license_full(SCM, SCM, SCM, SCM, SCM);
+extern SCM ag_scm_license_info(SCM, SCM, SCM, SCM, SCM);
+extern SCM ag_scm_license_name(SCM);
+extern SCM ag_scm_low_lim(SCM);
+extern SCM ag_scm_make_gperf(SCM, SCM);
+extern SCM ag_scm_make_header_guard(SCM);
+extern SCM ag_scm_make_tmp_dir(void);
+extern SCM ag_scm_makefile_script(SCM);
+extern SCM ag_scm_match_value_p(SCM, SCM, SCM);
+extern SCM ag_scm_max(SCM);
+extern SCM ag_scm_min(SCM);
+extern SCM ag_scm_out_delete(void);
+extern SCM ag_scm_out_depth(void);
+extern SCM ag_scm_out_emit_suspended(SCM);
+extern SCM ag_scm_out_line(void);
+extern SCM ag_scm_out_move(SCM);
+extern SCM ag_scm_out_name(void);
+extern SCM ag_scm_out_pop(SCM);
+extern SCM ag_scm_out_push_add(SCM);
+extern SCM ag_scm_out_push_new(SCM);
+extern SCM ag_scm_out_resume(SCM);
+extern SCM ag_scm_out_suspend(SCM);
+extern SCM ag_scm_out_switch(SCM);
+extern SCM ag_scm_output_file_next_line(SCM, SCM);
+extern SCM ag_scm_prefix(SCM, SCM);
+extern SCM ag_scm_printf(SCM, SCM);
+extern SCM ag_scm_raw_shell_str(SCM);
+extern SCM ag_scm_set_option(SCM);
+extern SCM ag_scm_set_writable(SCM);
+extern SCM ag_scm_shell(SCM);
+extern SCM ag_scm_shell_str(SCM);
+extern SCM ag_scm_shellf(SCM, SCM);
+extern SCM ag_scm_sprintf(SCM, SCM);
+extern SCM ag_scm_stack(SCM);
+extern SCM ag_scm_string_capitalize(SCM);
+extern SCM ag_scm_string_capitalize_x(SCM);
+extern SCM ag_scm_string_contains_eqv_p(SCM, SCM);
+extern SCM ag_scm_string_contains_p(SCM, SCM);
+extern SCM ag_scm_string_downcase(SCM);
+extern SCM ag_scm_string_downcase_x(SCM);
+extern SCM ag_scm_string_end_eqv_match_p(SCM, SCM);
+extern SCM ag_scm_string_end_match_p(SCM, SCM);
+extern SCM ag_scm_string_ends_eqv_p(SCM, SCM);
+extern SCM ag_scm_string_ends_with_p(SCM, SCM);
+extern SCM ag_scm_string_equals_p(SCM, SCM);
+extern SCM ag_scm_string_eqv_match_p(SCM, SCM);
+extern SCM ag_scm_string_eqv_p(SCM, SCM);
+extern SCM ag_scm_string_has_eqv_match_p(SCM, SCM);
+extern SCM ag_scm_string_has_match_p(SCM, SCM);
+extern SCM ag_scm_string_match_p(SCM, SCM);
+extern SCM ag_scm_string_start_eqv_match_p(SCM, SCM);
+extern SCM ag_scm_string_start_match_p(SCM, SCM);
+extern SCM ag_scm_string_starts_eqv_p(SCM, SCM);
+extern SCM ag_scm_string_starts_with_p(SCM, SCM);
+extern SCM ag_scm_string_substitute(SCM, SCM, SCM);
+extern SCM ag_scm_string_to_c_name_x(SCM);
+extern SCM ag_scm_string_to_camelcase(SCM);
+extern SCM ag_scm_string_tr(SCM, SCM, SCM);
+extern SCM ag_scm_string_tr_x(SCM, SCM, SCM);
+extern SCM ag_scm_string_upcase(SCM);
+extern SCM ag_scm_string_upcase_x(SCM);
+extern SCM ag_scm_sub_shell_str(SCM);
+extern SCM ag_scm_suffix(void);
+extern SCM ag_scm_sum(SCM);
+extern SCM ag_scm_time_string_to_number(SCM);
+extern SCM ag_scm_tpl_file(SCM);
+extern SCM ag_scm_tpl_file_line(SCM);
+extern SCM ag_scm_tpl_file_next_line(SCM);
+extern SCM ag_scm_version_compare(SCM, SCM, SCM);
+
+#endif /* GUILE_PROCS_EXPR_H_GUARD */
diff --git a/agen5/expr.ini b/agen5/expr.ini
new file mode 100644
index 0000000..3bd2c81
--- /dev/null
+++ b/agen5/expr.ini
@@ -0,0 +1,270 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (expr.ini)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:37 AM by AutoGen 5.16.2pre7
+ * From the definitions expr.def
+ * and the template file snarf
+ *
+ * copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Guile Initializations - Ag Global Variables
+ */
+#include "expr.h"
+typedef SCM (*scm_callback_t)(void);
+void ag_init(void);
+
+#if GUILE_VERSION >= 108000
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ scm_c_define_gsubr((char*)(_As), \
+ _Ar, _Ao, _Ax, (scm_callback_t)(void*)ag_scm_ ## _An)
+#else
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ gh_new_procedure((char*)(_As), (scm_callback_t)(void*)ag_scm_ ## _An, \
+ _Ar, _Ao, _Ax)
+#endif
+
+/*
+ * ag Initialization procedure.
+ */
+void
+ag_init(void)
+{
+static char const g_nm[1062] =
+/* 0 */ "ag-fprintf\0"
+/* 11 */ "ag-function?\0"
+/* 24 */ "agpl\0"
+/* 29 */ "base-name\0"
+/* 39 */ "bsd\0"
+/* 43 */ "c-string\0"
+/* 52 */ "chdir\0"
+/* 58 */ "count\0"
+/* 64 */ "def-file\0"
+/* 73 */ "def-file-line\0"
+/* 87 */ "dne\0"
+/* 91 */ "emit\0"
+/* 96 */ "error\0"
+/* 102 */ "error-source-line\0"
+/* 120 */ "exist?\0"
+/* 127 */ "extract\0"
+/* 135 */ "find-file\0"
+/* 145 */ "first-for?\0"
+/* 156 */ "for-by\0"
+/* 163 */ "for-from\0"
+/* 172 */ "for-index\0"
+/* 182 */ "for-sep\0"
+/* 190 */ "for-to\0"
+/* 197 */ "format-arg-count\0"
+/* 214 */ "fprintf\0"
+/* 222 */ "get\0"
+/* 226 */ "get-c-name\0"
+/* 237 */ "get-down-name\0"
+/* 251 */ "get-up-name\0"
+/* 263 */ "gperf\0"
+/* 269 */ "gpl\0"
+/* 273 */ "hide-email\0"
+/* 284 */ "high-lim\0"
+/* 293 */ "in?\0"
+/* 297 */ "join\0"
+/* 302 */ "kr-string\0"
+/* 312 */ "last-for?\0"
+/* 322 */ "len\0"
+/* 326 */ "lgpl\0"
+/* 331 */ "license\0"
+/* 339 */ "license-description\0"
+/* 359 */ "license-full\0"
+/* 372 */ "license-info\0"
+/* 385 */ "license-name\0"
+/* 398 */ "low-lim\0"
+/* 406 */ "make-gperf\0"
+/* 417 */ "make-header-guard\0"
+/* 435 */ "make-tmp-dir\0"
+/* 448 */ "makefile-script\0"
+/* 464 */ "match-value?\0"
+/* 477 */ "max\0"
+/* 481 */ "min\0"
+/* 485 */ "out-delete\0"
+/* 496 */ "out-depth\0"
+/* 506 */ "out-emit-suspended\0"
+/* 525 */ "out-line\0"
+/* 534 */ "out-move\0"
+/* 543 */ "out-name\0"
+/* 552 */ "out-pop\0"
+/* 560 */ "out-push-add\0"
+/* 573 */ "out-push-new\0"
+/* 586 */ "out-resume\0"
+/* 597 */ "out-suspend\0"
+/* 609 */ "out-switch\0"
+/* 620 */ "output-file-next-line\0"
+/* 642 */ "prefix\0"
+/* 649 */ "printf\0"
+/* 656 */ "raw-shell-str\0"
+/* 670 */ "set-option\0"
+/* 681 */ "set-writable\0"
+/* 694 */ "shell\0"
+/* 700 */ "shell-str\0"
+/* 710 */ "shellf\0"
+/* 717 */ "sprintf\0"
+/* 725 */ "stack\0"
+/* 731 */ "string-capitalize\0"
+/* 749 */ "string-capitalize!\0"
+/* 768 */ "*=*\0"
+/* 772 */ "*==*\0"
+/* 777 */ "string-downcase\0"
+/* 793 */ "string-downcase!\0"
+/* 810 */ "*~\0"
+/* 813 */ "*~~\0"
+/* 817 */ "*=\0"
+/* 820 */ "*==\0"
+/* 824 */ "==\0"
+/* 827 */ "~\0"
+/* 829 */ "=\0"
+/* 831 */ "*~*\0"
+/* 835 */ "*~~*\0"
+/* 840 */ "~~\0"
+/* 843 */ "~*\0"
+/* 846 */ "~~*\0"
+/* 850 */ "=*\0"
+/* 853 */ "==*\0"
+/* 857 */ "string-substitute\0"
+/* 875 */ "string->c-name!\0"
+/* 891 */ "string->camelcase\0"
+/* 909 */ "string-tr\0"
+/* 919 */ "string-tr!\0"
+/* 930 */ "string-upcase\0"
+/* 944 */ "string-upcase!\0"
+/* 959 */ "sub-shell-str\0"
+/* 973 */ "suffix\0"
+/* 980 */ "sum\0"
+/* 984 */ "time-string->number\0"
+/* 1004 */ "tpl-file\0"
+/* 1013 */ "tpl-file-line\0"
+/* 1027 */ "tpl-file-next-line\0"
+/* 1046 */ "version-compare";
+
+ NEW_PROC(g_nm + 0, 2, 0, 1, ag_fprintf);
+ NEW_PROC(g_nm + 11, 1, 0, 0, ag_function_p);
+ NEW_PROC(g_nm + 24, 2, 0, 0, agpl);
+ NEW_PROC(g_nm + 29, 0, 0, 0, base_name);
+ NEW_PROC(g_nm + 39, 3, 0, 0, bsd);
+ NEW_PROC(g_nm + 43, 1, 0, 0, c_string);
+ NEW_PROC(g_nm + 52, 1, 0, 0, chdir);
+ NEW_PROC(g_nm + 58, 1, 0, 0, count);
+ NEW_PROC(g_nm + 64, 0, 0, 0, def_file);
+ NEW_PROC(g_nm + 73, 1, 1, 0, def_file_line);
+ NEW_PROC(g_nm + 87, 1, 2, 0, dne);
+ NEW_PROC(g_nm + 91, 0, 0, 1, emit);
+ NEW_PROC(g_nm + 96, 1, 0, 0, error);
+ NEW_PROC(g_nm + 102, 0, 0, 0, error_source_line);
+ NEW_PROC(g_nm + 120, 1, 0, 0, exist_p);
+ NEW_PROC(g_nm + 127, 2, 2, 0, extract);
+ NEW_PROC(g_nm + 135, 1, 1, 0, find_file);
+ NEW_PROC(g_nm + 145, 0, 1, 0, first_for_p);
+ NEW_PROC(g_nm + 156, 1, 0, 0, for_by);
+ NEW_PROC(g_nm + 163, 1, 0, 0, for_from);
+ NEW_PROC(g_nm + 172, 0, 1, 0, for_index);
+ NEW_PROC(g_nm + 182, 1, 0, 0, for_sep);
+ NEW_PROC(g_nm + 190, 1, 0, 0, for_to);
+ NEW_PROC(g_nm + 197, 1, 0, 0, format_arg_count);
+ NEW_PROC(g_nm + 214, 2, 0, 1, fprintf);
+ NEW_PROC(g_nm + 222, 1, 1, 0, get);
+ NEW_PROC(g_nm + 226, 1, 0, 0, get_c_name);
+ NEW_PROC(g_nm + 237, 1, 0, 0, get_down_name);
+ NEW_PROC(g_nm + 251, 1, 0, 0, get_up_name);
+ NEW_PROC(g_nm + 263, 2, 0, 0, gperf);
+ NEW_PROC(g_nm + 269, 2, 0, 0, gpl);
+ NEW_PROC(g_nm + 273, 2, 0, 0, hide_email);
+ NEW_PROC(g_nm + 284, 1, 0, 0, high_lim);
+ NEW_PROC(g_nm + 293, 1, 0, 1, in_p);
+ NEW_PROC(g_nm + 297, 1, 0, 1, join);
+ NEW_PROC(g_nm + 302, 1, 0, 0, kr_string);
+ NEW_PROC(g_nm + 312, 0, 1, 0, last_for_p);
+ NEW_PROC(g_nm + 322, 1, 0, 0, len);
+ NEW_PROC(g_nm + 326, 3, 0, 0, lgpl);
+ NEW_PROC(g_nm + 331, 4, 0, 0, license);
+ NEW_PROC(g_nm + 339, 3, 1, 0, license_description);
+ NEW_PROC(g_nm + 359, 3, 2, 0, license_full);
+ NEW_PROC(g_nm + 372, 3, 2, 0, license_info);
+ NEW_PROC(g_nm + 385, 1, 0, 0, license_name);
+ NEW_PROC(g_nm + 398, 1, 0, 0, low_lim);
+ NEW_PROC(g_nm + 406, 1, 0, 1, make_gperf);
+ NEW_PROC(g_nm + 417, 1, 0, 0, make_header_guard);
+ NEW_PROC(g_nm + 435, 0, 0, 0, make_tmp_dir);
+ NEW_PROC(g_nm + 448, 1, 0, 0, makefile_script);
+ NEW_PROC(g_nm + 464, 3, 0, 0, match_value_p);
+ NEW_PROC(g_nm + 477, 0, 0, 1, max);
+ NEW_PROC(g_nm + 481, 0, 0, 1, min);
+ NEW_PROC(g_nm + 485, 0, 0, 0, out_delete);
+ NEW_PROC(g_nm + 496, 0, 0, 0, out_depth);
+ NEW_PROC(g_nm + 506, 1, 0, 0, out_emit_suspended);
+ NEW_PROC(g_nm + 525, 0, 0, 0, out_line);
+ NEW_PROC(g_nm + 534, 1, 0, 0, out_move);
+ NEW_PROC(g_nm + 543, 0, 0, 0, out_name);
+ NEW_PROC(g_nm + 552, 0, 1, 0, out_pop);
+ NEW_PROC(g_nm + 560, 1, 0, 0, out_push_add);
+ NEW_PROC(g_nm + 573, 0, 1, 0, out_push_new);
+ NEW_PROC(g_nm + 586, 1, 0, 0, out_resume);
+ NEW_PROC(g_nm + 597, 1, 0, 0, out_suspend);
+ NEW_PROC(g_nm + 609, 1, 0, 0, out_switch);
+ NEW_PROC(g_nm + 620, 0, 2, 0, output_file_next_line);
+ NEW_PROC(g_nm + 642, 2, 0, 0, prefix);
+ NEW_PROC(g_nm + 649, 1, 0, 1, printf);
+ NEW_PROC(g_nm + 656, 1, 0, 0, raw_shell_str);
+ NEW_PROC(g_nm + 670, 1, 0, 0, set_option);
+ NEW_PROC(g_nm + 681, 0, 1, 0, set_writable);
+ NEW_PROC(g_nm + 694, 1, 0, 0, shell);
+ NEW_PROC(g_nm + 700, 1, 0, 0, shell_str);
+ NEW_PROC(g_nm + 710, 1, 0, 1, shellf);
+ NEW_PROC(g_nm + 717, 1, 0, 1, sprintf);
+ NEW_PROC(g_nm + 725, 1, 0, 0, stack);
+ NEW_PROC(g_nm + 731, 1, 0, 0, string_capitalize);
+ NEW_PROC(g_nm + 749, 1, 0, 0, string_capitalize_x);
+ NEW_PROC(g_nm + 768, 2, 0, 0, string_contains_eqv_p);
+ NEW_PROC(g_nm + 772, 2, 0, 0, string_contains_p);
+ NEW_PROC(g_nm + 777, 1, 0, 0, string_downcase);
+ NEW_PROC(g_nm + 793, 1, 0, 0, string_downcase_x);
+ NEW_PROC(g_nm + 810, 2, 0, 0, string_end_eqv_match_p);
+ NEW_PROC(g_nm + 813, 2, 0, 0, string_end_match_p);
+ NEW_PROC(g_nm + 817, 2, 0, 0, string_ends_eqv_p);
+ NEW_PROC(g_nm + 820, 2, 0, 0, string_ends_with_p);
+ NEW_PROC(g_nm + 824, 2, 0, 0, string_equals_p);
+ NEW_PROC(g_nm + 827, 2, 0, 0, string_eqv_match_p);
+ NEW_PROC(g_nm + 829, 2, 0, 0, string_eqv_p);
+ NEW_PROC(g_nm + 831, 2, 0, 0, string_has_eqv_match_p);
+ NEW_PROC(g_nm + 835, 2, 0, 0, string_has_match_p);
+ NEW_PROC(g_nm + 840, 2, 0, 0, string_match_p);
+ NEW_PROC(g_nm + 843, 2, 0, 0, string_start_eqv_match_p);
+ NEW_PROC(g_nm + 846, 2, 0, 0, string_start_match_p);
+ NEW_PROC(g_nm + 850, 2, 0, 0, string_starts_eqv_p);
+ NEW_PROC(g_nm + 853, 2, 0, 0, string_starts_with_p);
+ NEW_PROC(g_nm + 857, 3, 0, 0, string_substitute);
+ NEW_PROC(g_nm + 875, 1, 0, 0, string_to_c_name_x);
+ NEW_PROC(g_nm + 891, 1, 0, 0, string_to_camelcase);
+ NEW_PROC(g_nm + 909, 3, 0, 0, string_tr);
+ NEW_PROC(g_nm + 919, 3, 0, 0, string_tr_x);
+ NEW_PROC(g_nm + 930, 1, 0, 0, string_upcase);
+ NEW_PROC(g_nm + 944, 1, 0, 0, string_upcase_x);
+ NEW_PROC(g_nm + 959, 1, 0, 0, sub_shell_str);
+ NEW_PROC(g_nm + 973, 0, 0, 0, suffix);
+ NEW_PROC(g_nm + 980, 0, 0, 1, sum);
+ NEW_PROC(g_nm + 984, 1, 0, 0, time_string_to_number);
+ NEW_PROC(g_nm +1004, 0, 1, 0, tpl_file);
+ NEW_PROC(g_nm +1013, 0, 1, 0, tpl_file_line);
+ NEW_PROC(g_nm +1027, 0, 1, 0, tpl_file_next_line);
+ NEW_PROC(g_nm +1046, 3, 0, 0, version_compare);
+}
+#undef NEW_PROC
+/* end of expr.ini */
diff --git a/agen5/fmemopen.c b/agen5/fmemopen.c
new file mode 100644
index 0000000..6e865be
--- /dev/null
+++ b/agen5/fmemopen.c
@@ -0,0 +1,743 @@
+#if defined(ENABLE_FMEMOPEN)
+#include <sys/ioctl.h>
+
+typedef enum {
+ FMEMC_INVALID = 0,
+ FMEMC_GET_BUF_ADDR
+} fmemctl_t;
+
+typedef struct {
+ enum { FMEMC_GBUF_LEAVE_OWNERSHIP,
+ FMEMC_GBUF_TAKE_OWNERSHIP
+ } own;
+ char * buffer;
+ size_t buf_size;
+ size_t eof;
+} fmemc_get_buf_addr_t;
+
+#ifdef HURD
+#define _IOT__IOTBASE_fmemc_get_buf_addr_t sizeof(fmemc_get_buf_addr_t)
+#endif
+
+#define IOCTL_FMEMC_GET_BUF_ADDR \
+ _IOWR('m', FMEMC_GET_BUF_ADDR, fmemc_get_buf_addr_t)
+
+/**
+ * @file /old-home/bkorb/tools/mine/lib/fmemopen/fmemopen.c
+ *
+ * Copyright (c) 2004-2012 by Bruce Korb. All rights reserved.
+ *
+ * This code was inspired from software written by
+ * Hanno Mueller, kontakt@hanno.de
+ * and completely rewritten by Bruce Korb, bkorb@gnu.org
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if defined(HAVE_FOPENCOOKIE)
+# if defined(HAVE_LIBIO_H)
+# include <libio.h>
+# endif
+
+ typedef off64_t * seek_off_t;
+ typedef int seek_ret_t;
+
+#elif defined(HAVE_FUNOPEN)
+ typedef fpos_t seek_off_t;
+ typedef fpos_t seek_ret_t;
+
+# ifdef NEED_COOKIE_FUNCTION_TYPEDEFS
+ typedef int (cookie_read_function_t )(void *, char *, int);
+ typedef int (cookie_write_function_t)(void *, char const *, int);
+ typedef fpos_t (cookie_seek_function_t )(void *, fpos_t, int);
+ typedef int (cookie_close_function_t)(void *);
+# endif /* NEED_COOKIE_FUNCTION_TYPEDEFS */
+#endif
+
+#define PROP_TABLE \
+_Prop_( read, "Read from buffer" ) \
+_Prop_( write, "Write to buffer" ) \
+_Prop_( append, "Append to buffer okay" ) \
+_Prop_( binary, "byte data - not string" ) \
+_Prop_( create, "allocate the string" ) \
+_Prop_( truncate, "start writing at start" ) \
+_Prop_( allocated, "we allocated the buffer" ) \
+_Prop_( fixed_size, "writes do not append" )
+
+#define _Prop_(n,s) BIT_ID_ ## n,
+typedef enum { PROP_TABLE BIT_CT } fmem_flags_e;
+#undef _Prop_
+
+#define FLAG_BIT(n) (1 << BIT_ID_ ## n)
+
+typedef unsigned long mode_bits_t;
+typedef unsigned char buf_bytes_t;
+
+typedef struct fmem_cookie_s fmem_cookie_t;
+struct fmem_cookie_s {
+ mode_bits_t mode;
+ buf_bytes_t * buffer;
+ size_t buf_size; /* Full size of buffer */
+ size_t next_ix; /* Current position */
+ size_t eof; /* End Of File */
+ size_t pg_size; /* size of a memory page.
+ Future architectures allow it to vary
+ by memory region. */
+};
+
+typedef struct {
+ FILE * fp;
+ fmem_cookie_t * cookie;
+} cookie_fp_map_t;
+
+static cookie_fp_map_t const * map = NULL;
+static unsigned int map_ct = 0;
+static unsigned int map_alloc_ct = 0;
+
+/* = = = START-STATIC-FORWARD = = = */
+static int
+fmem_getmode(char const * mode, mode_bits_t * pRes);
+
+static int
+fmem_extend(fmem_cookie_t *pFMC, size_t new_size);
+
+static ssize_t
+fmem_read(void *cookie, void *pBuf, size_t sz);
+
+static ssize_t
+fmem_write(void *cookie, const void *pBuf, size_t sz);
+
+static seek_ret_t
+fmem_seek(void * cookie, seek_off_t offset, int dir);
+
+static int
+fmem_close(void * cookie);
+
+static bool
+fmem_config_user_buf(fmem_cookie_t * pFMC, void * buf, ssize_t len);
+
+static bool
+fmem_alloc_buf(fmem_cookie_t * pFMC, ssize_t len);
+/* = = = END-STATIC-FORWARD = = = */
+
+#ifdef TEST_FMEMOPEN
+ static fmem_cookie_t* saved_cookie = NULL;
+#endif
+
+/**
+ * Convert a mode string into mode bits.
+ */
+static int
+fmem_getmode(char const * mode, mode_bits_t * pRes)
+{
+ if (mode == NULL)
+ return 1;
+
+ switch (*mode) {
+ case 'a': *pRes = FLAG_BIT(write) | FLAG_BIT(append);
+ break;
+ case 'w': *pRes = FLAG_BIT(write) | FLAG_BIT(truncate);
+ break;
+ case 'r': *pRes = FLAG_BIT(read);
+ break;
+ default: return EINVAL;
+ }
+
+ /*
+ * If someone wants to supply a "wxxbxbbxbb+" mode string, I don't care.
+ */
+ for (;;) {
+ switch (*++mode) {
+ case '+': *pRes |= FLAG_BIT(read) | FLAG_BIT(write);
+ if (mode[1] != NUL)
+ return EINVAL;
+ break;
+ case NUL: break;
+ case 'b': *pRes |= FLAG_BIT(binary); continue;
+ case 'x': continue;
+ default: return EINVAL;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Extend the space associated with an fmem file.
+ */
+static int
+fmem_extend(fmem_cookie_t *pFMC, size_t new_size)
+{
+ size_t ns = (new_size + (pFMC->pg_size - 1)) & (~(pFMC->pg_size - 1));
+
+ /*
+ * We can expand the buffer only if we are in append mode.
+ */
+ if (pFMC->mode & FLAG_BIT(fixed_size))
+ goto no_space;
+
+ if ((pFMC->mode & FLAG_BIT(allocated)) == 0) {
+ /*
+ * Previously, this was a user supplied buffer. We now move to one
+ * of our own. The user is responsible for the earlier memory.
+ */
+ void* bf = malloc(ns);
+ if (bf == NULL)
+ goto no_space;
+
+ memcpy(bf, pFMC->buffer, pFMC->buf_size);
+ pFMC->buffer = bf;
+ pFMC->mode |= FLAG_BIT(allocated);
+ }
+ else {
+ void* bf = realloc(pFMC->buffer, ns);
+ if (bf == NULL)
+ goto no_space;
+
+ pFMC->buffer = bf;
+ }
+
+ /*
+ * Unallocated file space is set to zeros. Emulate that.
+ */
+ memset(pFMC->buffer + pFMC->buf_size, 0, ns - pFMC->buf_size);
+ pFMC->buf_size = ns;
+ return 0;
+
+ no_space:
+ errno = ENOSPC;
+ return -1;
+}
+
+/**
+ * Handle file system callback to read data from our string.
+ */
+static ssize_t
+fmem_read(void *cookie, void *pBuf, size_t sz)
+{
+ fmem_cookie_t *pFMC = cookie;
+
+ if (pFMC->next_ix + sz > pFMC->eof) {
+ if (pFMC->next_ix >= pFMC->eof)
+ return (sz > 0) ? -1 : 0;
+ sz = pFMC->eof - pFMC->next_ix;
+ }
+
+ memcpy(pBuf, pFMC->buffer + pFMC->next_ix, sz);
+
+ pFMC->next_ix += sz;
+
+ return sz;
+}
+
+/**
+ * Handle file system callback to write data to our string
+ */
+static ssize_t
+fmem_write(void *cookie, const void *pBuf, size_t sz)
+{
+ fmem_cookie_t *pFMC = cookie;
+ int add_nul_char;
+
+ /*
+ * In append mode, always seek to the end before writing.
+ */
+ if (pFMC->mode & FLAG_BIT(append))
+ pFMC->next_ix = pFMC->eof;
+
+ /*
+ * Only add a NUL character if:
+ *
+ * * we are not in binary mode
+ * * there are data to write
+ * * the last character to write is not already NUL
+ */
+ add_nul_char =
+ ((pFMC->mode & FLAG_BIT(binary)) != 0)
+ && (sz > 0)
+ && (((char*)pBuf)[sz - 1] != NUL);
+
+ {
+ size_t next_pos = pFMC->next_ix + sz + add_nul_char;
+ if (next_pos > pFMC->buf_size) {
+ if (fmem_extend(pFMC, next_pos) != 0) {
+ /*
+ * We could not extend the memory. Try to write some data.
+ * Fail if we are either at the end or not writing data.
+ */
+ if ((pFMC->next_ix >= pFMC->buf_size) || (sz == 0))
+ return -1; /* no space at all. errno is set. */
+
+ /*
+ * Never add the NUL for a truncated write. "sz" may be
+ * unchanged or limited here.
+ */
+ add_nul_char = 0;
+ sz = pFMC->buf_size - pFMC->next_ix;
+ }
+ }
+ }
+
+ memcpy(pFMC->buffer + pFMC->next_ix, pBuf, sz);
+
+ pFMC->next_ix += sz;
+
+ /*
+ * Check for new high water mark and remember it. Add a NUL if
+ * we do that and if we have a new high water mark.
+ */
+ if (pFMC->next_ix > pFMC->eof) {
+ pFMC->eof = pFMC->next_ix;
+ if (add_nul_char)
+ /*
+ * There is space for this NUL. The "add_nul_char" is not part of
+ * the "sz" that was added to "next_ix".
+ */
+ pFMC->buffer[ pFMC->eof ] = NUL;
+ }
+
+ return sz;
+}
+
+/**
+ * Handle file system callback to set a new current position
+ */
+static seek_ret_t
+fmem_seek(void * cookie, seek_off_t offset, int dir)
+{
+ size_t new_pos;
+ fmem_cookie_t *pFMC = cookie;
+
+#ifdef HAVE_FOPENCOOKIE
+ /*
+ * GNU interface: offset passed and returned by address.
+ */
+ switch (dir) {
+ case SEEK_SET: new_pos = *offset; break;
+ case SEEK_CUR: new_pos = pFMC->next_ix + *offset; break;
+ case SEEK_END: new_pos = pFMC->eof - *offset; break;
+
+ default:
+ goto seek_oops;
+ }
+#else
+ /*
+ * BSD interface: offset passed by value, returned as retval.
+ */
+ switch (dir) {
+ case SEEK_SET: new_pos = offset; break;
+ case SEEK_CUR: new_pos = pFMC->next_ix + offset; break;
+ case SEEK_END: new_pos = pFMC->eof - offset; break;
+
+ default:
+ goto seek_oops;
+ }
+#endif
+
+ if ((signed)new_pos < 0)
+ goto seek_oops;
+
+ if (new_pos > pFMC->buf_size) {
+ if (fmem_extend(pFMC, new_pos))
+ return -1; /* errno is set */
+ }
+
+ pFMC->next_ix = new_pos;
+
+#ifdef HAVE_FOPENCOOKIE
+ *offset = (off64_t)new_pos;
+ return 0;
+#else
+ return new_pos;
+#endif
+
+ seek_oops:
+ errno = EINVAL;
+ return -1;
+}
+
+/**
+ * Free up the memory associated with an fmem file.
+ * If the user is managing the space, then the allocated bit is set.
+ */
+static int
+fmem_close(void * cookie)
+{
+ fmem_cookie_t * pFMC = cookie;
+ cookie_fp_map_t * pmap = (void *)map;
+ unsigned int mct = map_ct;
+
+ while (mct-- != 0) {
+ if (pmap->cookie == cookie) {
+ *pmap = map[--map_ct];
+ break;
+ }
+ pmap++;
+ }
+
+ if (mct > map_ct)
+ errno = EINVAL;
+
+ if (pFMC->mode & FLAG_BIT(allocated))
+ free(pFMC->buffer);
+ free(pFMC);
+
+ return 0;
+}
+
+/**
+ * Configure the user supplied buffer.
+ */
+static bool
+fmem_config_user_buf(fmem_cookie_t * pFMC, void * buf, ssize_t len)
+{
+ /*
+ * User allocated buffer. User responsible for disposal.
+ */
+ if (len == 0) {
+ free(pFMC);
+ errno = EINVAL;
+ return false;
+ }
+
+ pFMC->buffer = (buf_bytes_t*)buf;
+
+ /* Figure out where our "next byte" and EOF are.
+ * Truncated files start at the beginning.
+ */
+ if (pFMC->mode & FLAG_BIT(truncate)) {
+ /*
+ * "write" mode
+ */
+ pFMC->eof = \
+ pFMC->next_ix = 0;
+ }
+
+ else if (pFMC->mode & FLAG_BIT(binary)) {
+ pFMC->eof = len;
+ pFMC->next_ix = (pFMC->mode & FLAG_BIT(append)) ? len : 0;
+
+ } else {
+ /*
+ * append or read text mode -- find the end of the buffer
+ * (the first NUL character)
+ */
+ buf_bytes_t *p = (buf_bytes_t*)buf;
+
+ pFMC->eof = 0;
+ while ((*p != NUL) && (++(pFMC->eof) < (size_t)len)) p++;
+ pFMC->next_ix =
+ (pFMC->mode & FLAG_BIT(append)) ? pFMC->eof : 0;
+ }
+
+ /*
+ * text mode - NUL terminate buffer, if it fits.
+ */
+ if ( ((pFMC->mode & FLAG_BIT(binary)) == 0)
+ && (pFMC->next_ix < (size_t)len)) {
+ pFMC->buffer[pFMC->next_ix] = NUL;
+ }
+
+ pFMC->buf_size = len;
+ return true;
+}
+
+/**
+ * Allocate an initial buffer for fmem.
+ */
+static bool
+fmem_alloc_buf(fmem_cookie_t * pFMC, ssize_t len)
+{
+ /*
+ * We must allocate the buffer. If "len" is zero, set it to page size.
+ */
+ pFMC->mode |= FLAG_BIT(allocated);
+ if (len == 0)
+ len = pFMC->pg_size;
+
+ /*
+ * Unallocated file space is set to NULs. Emulate that.
+ */
+ pFMC->buffer = calloc((size_t)1, (size_t)len);
+ if (pFMC->buffer == NULL) {
+ errno = ENOMEM;
+ free(pFMC);
+ return false;
+ }
+
+ /*
+ * We've allocated the buffer. The end of file and next entry
+ * are both zero.
+ */
+ pFMC->next_ix = 0;
+ pFMC->eof = 0;
+ pFMC->buf_size = len;
+ return true;
+}
+
+/*=export_func ag_fmemopen
+ *
+ * what: Open a stream to a string
+ *
+ * arg: + void* + buf + buffer to use for i/o +
+ * arg: + ssize_t + len + size of the buffer +
+ * arg: + char* + mode + mode string, a la fopen(3C) +
+ *
+ * ret-type: FILE*
+ * ret-desc: a stdio FILE* pointer
+ *
+ * err: NULL is returned and errno is set to @code{EINVAL} or @code{ENOSPC}.
+ *
+ * doc:
+ *
+ * This function requires underlying @var{libc} functionality:
+ * either @code{fopencookie(3GNU)} or @code{funopen(3BSD)}.
+ *
+ * If @var{buf} is @code{NULL}, then a buffer is allocated. The initial
+ * allocation is @var{len} bytes. If @var{len} is less than zero, then the
+ * buffer will be reallocated as more space is needed. Any allocated
+ * memory is @code{free()}-ed when @code{fclose(3C)} is called.
+ *
+ * If @code{buf} is not @code{NULL}, then @code{len} must not be zero.
+ * It may still be less than zero to indicate that the buffer may
+ * be reallocated.
+ *
+ * The mode string is interpreted as follows. If the first character of
+ * the mode is:
+ *
+ * @table @code
+ * @item a
+ * Then the string is opened in "append" mode. In binary mode, "appending"
+ * will begin from the end of the initial buffer. Otherwise, appending will
+ * start at the first NUL character in the initial buffer (or the end of the
+ * buffer if there is no NUL character). Do not use fixed size buffers
+ * (negative @var{len} lengths) in append mode.
+ *
+ * @item w
+ * Then the string is opened in "write" mode. Any initial buffer is presumed
+ * to be empty.
+ *
+ * @item r
+ * Then the string is opened in "read" mode.
+ * @end table
+ *
+ * @noindent
+ * If it is not one of these three, the open fails and @code{errno} is
+ * set to @code{EINVAL}. These initial characters may be followed by:
+ *
+ * @table @code
+ * @item +
+ * The buffer is marked as updatable and both reading and writing is enabled.
+ *
+ * @item b
+ * The I/O is marked as "binary" and a trailing NUL will not be inserted
+ * into the buffer. Without this mode flag, one will be inserted after the
+ * @code{EOF}, if it fits. It will fit if the buffer is extensible (the
+ * provided @var{len} was negative). This mode flag has no effect if
+ * the buffer is opened in read-only mode.
+ *
+ * @item x
+ * This is ignored.
+ * @end table
+ *
+ * @noindent
+ * Any other letters following the inital 'a', 'w' or 'r' will cause an error.
+=*/
+FILE *
+ag_fmemopen(void * buf, ssize_t len, char const * mode)
+{
+ fmem_cookie_t *pFMC;
+
+ {
+ mode_bits_t bits;
+
+ if (fmem_getmode(mode, &bits) != 0) {
+ return NULL;
+ }
+
+ pFMC = malloc(sizeof(fmem_cookie_t));
+ if (pFMC == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ pFMC->mode = bits;
+ }
+
+ /*
+ * Two more mode bits that do not come from the mode string:
+ * a negative size implies fixed size buffer and a NULL
+ * buffer pointer means we must allocate (and free) it.
+ */
+ if (len <= 0) {
+ /*
+ * We only need page size if we might extend an allocation.
+ */
+ len = -len;
+ pFMC->pg_size = getpagesize();
+ }
+
+ else {
+ pFMC->mode |= FLAG_BIT(fixed_size);
+ }
+
+ if (buf != NULL) {
+ if (! fmem_config_user_buf(pFMC, buf, len))
+ return NULL;
+
+ } else if ((pFMC->mode & (FLAG_BIT(append) | FLAG_BIT(truncate))) == 0) {
+ /*
+ * Not appending and not truncating. We must be reading.
+ * We also have no user supplied buffer. Nonsense.
+ */
+ errno = EINVAL;
+ free(pFMC);
+ return NULL;
+ }
+
+ else if (! fmem_alloc_buf(pFMC, len))
+ return NULL;
+
+#ifdef TEST_FMEMOPEN
+ saved_cookie = pFMC;
+#endif
+
+ {
+ FILE * res;
+
+ cookie_read_function_t* pRd = (pFMC->mode & FLAG_BIT(read))
+ ? (cookie_read_function_t*)fmem_read : NULL;
+ cookie_write_function_t* pWr = (pFMC->mode & FLAG_BIT(write))
+ ? (cookie_write_function_t*)fmem_write : NULL;
+
+#ifdef HAVE_FOPENCOOKIE
+ cookie_io_functions_t iof;
+ iof.read = pRd;
+ iof.write = pWr;
+ iof.seek = fmem_seek;
+ iof.close = fmem_close;
+
+ res = fopencookie(pFMC, mode, iof);
+#else
+ res = funopen(pFMC, pRd, pWr, fmem_seek, fmem_close);
+#endif
+ if (res == NULL)
+ return res;
+
+ if (++map_ct >= map_alloc_ct) {
+ void * p = (map_alloc_ct > 0)
+ ? realloc((void *)map, (map_alloc_ct += 4) * sizeof(*map))
+ : malloc((map_alloc_ct = 4) * sizeof(*map));
+
+ if (p == NULL) {
+ fclose(res);
+ errno = ENOMEM; /* "fclose" set it to "EINVAL". */
+ return NULL;
+ }
+
+ map = p;
+ }
+
+ {
+ cookie_fp_map_t * p = (void *)(map + map_ct - 1);
+ p->fp = res;
+ p->cookie = pFMC;
+ }
+
+ return res;
+ }
+}
+
+/*=export_func ag_fmemioctl
+ *
+ * what: perform an ioctl on a FILE* descriptor
+ *
+ * arg: + FILE* + fp + file pointer +
+ * arg: + int + req + ioctl command +
+ * arg: + ... + varargs + arguments for command +
+ *
+ * ret-type: int
+ * ret-desc: zero on success, otherwise error in errno
+ *
+ * err: errno is set to @code{EINVAL} or @code{ENOSPC}.
+ *
+ * doc:
+ *
+ * The file pointer passed in must have been returned by ag_fmemopen.
+=*/
+int
+ag_fmemioctl(FILE * fp, int req, ...)
+{
+ fmem_cookie_t * cookie;
+ fmemc_get_buf_addr_t * gba;
+
+ if ((unsigned int)req != IOCTL_FMEMC_GET_BUF_ADDR) {
+ /*
+ * It is not any of the IOCTL commands we know about.
+ */
+ errno = EINVAL;
+ return -1;
+ }
+
+ {
+ cookie_fp_map_t const * pmap = map;
+ unsigned int mct = map_ct;
+
+ for (;;) {
+ if (mct-- == 0) {
+ /*
+ * fmemopen didn't create this FILE*, so it is invalid.
+ */
+ errno = EINVAL;
+ return -1;
+ }
+ if (pmap->fp == fp)
+ break;
+ pmap++;
+ }
+
+ cookie = pmap->cookie;
+ }
+
+ {
+ va_list ap;
+ va_start(ap, req);
+ gba = va_arg(ap, fmemc_get_buf_addr_t *);
+ va_end(ap);
+ }
+
+ gba->buffer = (char *)(cookie->buffer);
+ gba->buf_size = cookie->buf_size;
+ gba->eof = cookie->eof;
+ if (gba->own != FMEMC_GBUF_LEAVE_OWNERSHIP)
+ cookie->mode &= ~FLAG_BIT(allocated);
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of fmemopen.c */
+#endif /* ENABLE_FMEMOPEN */
diff --git a/agen5/fsm-macro.tlib b/agen5/fsm-macro.tlib
new file mode 100644
index 0000000..24315ef
--- /dev/null
+++ b/agen5/fsm-macro.tlib
@@ -0,0 +1,402 @@
+[= AutoGen5 Template -*- Mode: Scheme -*-
+
+#; Time-stamp: "2012-03-31 17:04:56 bkorb"
+#;
+#; This file is part of AutoGen.
+#; AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+#;
+#; AutoGen is free software: you can redistribute it and/or modify it
+#; under the terms of the GNU General Public License as published by the
+#; Free Software Foundation, either version 3 of the License, or
+#; (at your option) any later version.
+#;
+#; AutoGen is distributed in the hope that it will be useful, but
+#; WITHOUT ANY WARRANTY; without even the implied warranty of
+#; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#; See the GNU General Public License for more details.
+#;
+#; You should have received a copy of the GNU General Public License along
+#; with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=][=
+
+(define exit-proc (get "exit-proc" "exit"))
+
+=][=
+
+DEFINE emit-invalid-msg =][=
+
+ ;; "defs" indexes will be diverted.
+ (define tmp-text "")
+ (define str-table (string-append "z" Pfx "Strings"))
+ (string-table-new str-table)
+ (define add-tbl-str (lambda (ent-str)
+ (set! index-list (string-append index-list
+ (number->string (string-table-add str-table ent-str)) "\n" )) ))
+
+ (string-table-add str-table "** OUT-OF-RANGE **")
+
+ (ag-fprintf 0 "\n#define %sFsmErr_off %d\n" Pfx
+ (string-table-add str-table
+ "FSM Error: in state %d (%s), event %d (%s) is invalid\n"))
+
+ (define invalid-ix (string-table-add str-table "invalid"))
+ (ag-fprintf 0 "#define %sEvInvalid_off %d\n" Pfx invalid-ix)
+
+ (define index-list (sprintf "%d\n" (string-table-add str-table "init")))
+ (ag-fprintf 0 "#define %sStInit_off %s\n" Pfx index-list)
+
+ (out-push-new)
+
+=][=
+
+ FOR state =][=
+ (add-tbl-str (get-down "state")) =][=
+ ENDFOR state =][=
+
+ (ag-fprintf 0 "\nstatic const size_t asz%sStates[%d] = {\n"
+ Pfx (+ 1 (count "state")) )
+ (emit (shell (string-append
+ "${CLexe-columns} --spread=1 -I4 -S, <<_EOF_\n"
+ index-list
+ "_EOF_"
+ )))
+
+ (set! index-list "")
+
+ " };\n" =][=
+
+ FOR event =][=
+
+ (set! tmp-text (get "event"))
+ (set! tmp-text
+ (if (exist? tmp-text) (get tmp-text) (string-downcase! tmp-text)))
+ (add-tbl-str tmp-text)
+ =][=
+
+ ENDFOR event =][=
+
+ (ag-fprintf 0 "\nstatic const size_t asz%sEvents[%d] = {\n"
+ Pfx (+ 1 (count "event")) )
+
+ (emit (shell (string-append
+ "${CLexe-columns} --spread=1 -I4 -S, <<_EOF_\n"
+ index-list
+ (sprintf "%d\n" invalid-ix)
+ "_EOF_")))
+ (emit " };\n")
+
+ (out-suspend "strings")
+ (emit-string-table str-table)
+ (out-resume "strings")
+ (emit (out-pop #t)) =]
+
+#define [=(. PFX)
+ =]_EVT_NAME(t) ( (((unsigned)(t)) >= [=(+ 1 (count "event"))=]) \
+ ? z[=(. Pfx)=]Strings : z[=(. Pfx)
+ =]Strings + asz[=(. Pfx)=]Events[t])
+
+#define [=(. PFX)
+ =]_STATE_NAME(s) ( (((unsigned)(s)) >= [=(+ 1 (count "state"))=]) \
+ ? z[=(. Pfx)=]Strings : z[=(. Pfx)
+ =]Strings + asz[=(. Pfx)=]States[s])
+
+#ifndef EXIT_FAILURE
+# define EXIT_FAILURE 1
+#endif
+
+static int [=(. pfx)=]_invalid_transition( te_[=(. pfx)=]_state st, te_[=
+ (. pfx)=]_event evt );
+[=
+
+ IF (not (exist? "handler-file")) =]
+/* * * * * * * * * THE CODE STARTS HERE * * * * * * * *
+ *
+ * Print out an invalid transition message and return EXIT_FAILURE
+ */
+static int
+[=(. pfx)=]_invalid_transition( te_[=(. pfx)=]_state st, te_[=
+ (. pfx)=]_event evt )
+{
+[=
+(set! tmp-text (sprintf "\
+ char const * fmt = z%1$sStrings + %1$sFsmErr_off;
+ fprintf( stderr, fmt, st, %2$s_STATE_NAME(st), evt, %2$s_EVT_NAME(evt));"
+ Pfx PFX ))
+
+(extract fsm-source " /* %s == INVALID TRANS MSG == %s */" "" tmp-text)=]
+
+ return EXIT_FAILURE;
+}
+[=
+
+ ENDIF not exist handler-file =][=
+
+ENDDEF emit-invalid-msg
+
+;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE emit-cookie-args =][=
+ FOR cookie =]
+ [=cookie=],[=
+ ENDFOR =][=
+ENDDEF emit-cookie-args
+
+;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE build-callback =][=
+
+ CASE cb-name =][=
+ = noop =][=
+ * =]
+static te_[=(. pfx)=]_state
+[=cb_prefix=]_[=cb_name=]([= emit-cookie-args =]
+ te_[=(. pfx)=]_state initial,
+ te_[=(. pfx)=]_state maybe_next,
+ te_[=(. pfx)=]_event trans_evt )
+{
+[= (extract fsm-source (string-append
+"/* %s == " (string-tr! (get "cb_name") "a-z_-" "A-Z ") " == %s */" )
+""
+(if (= (get "cb-name") "invalid")
+ (string-append " " exit-proc "(" pfx
+ "_invalid_transition(initial, trans_evt));")
+ " return maybe_next;" )) =]
+}
+[=
+ ESAC =][=
+
+ENDDEF build-callback
+
+# # # # # # # =][=
+
+DEFINE callbacks =][=
+
+ `set -- \`sed 's/,//' ${tmp_dir}/xlist\`` =][=
+
+ WHILE `echo $#` =][=
+
+ INVOKE build-callback
+ cb_prefix = (string-append pfx "_do")
+ cb_name = (shell "echo $1 ; shift") =][=
+
+ ENDWHILE echo $# =][=
+
+ENDDEF callbacks
+
+# # # # # # # =][=
+
+DEFINE run-callback
+
+=]
+ if (pT != NULL)
+ nxtSt = (*pT)( [=
+ FOR cookie =][=
+ (shellf "echo '%s'|sed 's,.*[ \t*],,'" (get "cookie")) =], [=
+
+ ENDFOR =][=(. pfx)=]_state, nxtSt, trans_evt );[=
+
+ENDDEF run-callback
+
+;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE build-switch =]
+ case [=cb_prefix=]_[=cb_name=]:[=
+
+ IF (== (get "cb_name") "NOOP") =] break;[=
+ ELSE =]
+[=
+(set! example-code (if (= (get "cb_name") "invalid") (string-append
+ " " exit-proc "(" pfx "_invalid_transition(" pfx
+ "_state, trans_evt));")
+ (string-append " nxtSt = HANDLE_" (get "cb_name") "();") ))
+
+(extract fsm-source
+ (string-append " /* %s == " (get "cb_name") " == %s */")
+ "" example-code ) =]
+ break;
+
+[=ENDIF=][=
+
+ENDDEF build-switch
+
+;;; # # # # # =][=
+
+DEFINE run-switch =][=
+
+(define example-code "") =]
+
+ switch (trans) {[=
+ `set -- \`sed 's/,//' ${tmp_dir}/xlist\`` =][=
+
+ WHILE `echo $#` =][=
+
+ invoke build-switch
+ cb_prefix = (string-append PFX "_TR")
+ cb_name = (shell "echo $1 ; shift") =][=
+
+ ENDWHILE echo $# =]
+ default:
+[=(extract fsm-source " /* %s == BROKEN MACHINE == %s */" ""
+ (string-append " " exit-proc "(" pfx "_invalid_transition("
+ pfx "_state, trans_evt));" ))=]
+ }[=
+
+ENDDEF run-switch
+
+;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE preamble =][=
+
+IF (emit (dne " * " "/* "))
+ (== (suffix) "c") =][=
+
+(set! fsm-source (string-append tmp-dir "/fsm.code"))
+(set-writable) =][=
+
+ELSE =][=
+
+(make-tmp-dir)
+(define fsm-source (string-append tmp-dir "/fsm.header"))
+(shellf "test -f %1$s-fsm.h && cp %1$s-fsm.h ${tmp_dir}/fsm.header
+ test -f %1$s-fsm.c && cp %1$s-fsm.c ${tmp_dir}/fsm.code" (base-name))
+
+(define pfx (string->c-name! (string-downcase!
+ (if (exist? "prefix") (get "prefix") (base-name)) )))
+(define PFX (string-upcase pfx))
+(define Pfx (string-capitalize pfx))
+(define t-trans (string-append "t_" pfx "_transition"))
+
+=][=
+
+ENDIF =]
+ *
+ * Automated Finite State Machine
+ *
+[=(license-description "mbsd" "AutoFSM" " * " "Bruce Korb")=]
+ */[=
+
+ENDDEF preamble
+
+;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE compute-transitions =][=
+
+;;; Initialize every possible transition as invalid
+;;;
+(define tr-name (if (=* (get "method") "call")
+ (string-append pfx "_do_invalid")
+ (string-append PFX "_TR_INVALID") ))
+(shellf
+ "ev_list='%s' ; st_list='INIT %s'
+ for f in $ev_list ; do for g in $st_list
+ do eval FSM_TRANS_${g}_${f}=\"'{ %s_ST_INVALID, %s }'\"
+ export FSM_TRANS_${g}_${f}
+ done ; done"
+
+ (stack-up "event")
+ (stack-up "state")
+ PFX tr-name )
+
+(define tev "")
+(define tst "")
+(define ttype "")
+(define next "")
+(define proc-ptr-type
+ (lambda (tp)
+ (if (= tp "noop") "NULL"
+ (string-downcase! (string-append pfx "_do_" tp)) )))
+
+=][=#
+;;; Now replace the initial values with proper ones gotten from
+;;; the trasition definitions.
+;;;
+;;; It is actually possible to have multiple specifications for a
+;;; single state/event pair, however the last state/event assignment
+;;; will supply the value for the transition table. Different
+;;; transitions may also specify the same transition method.
+;;; For that, we unique sort the list and eliminate dups.
+;;; The unique-ified list is used to produce the callout table.
+;;;
+=][=
+FOR transition =][=
+
+ IF (== (get "tst") "*") =][=
+
+ ;; This transition applies for all states
+ ;;
+ (set! tev (get-up "tev"))
+ (set! ttype (if (exist? "ttype") (get "ttype")
+ (string-append "${f}_" tev) ))
+
+ (set! tr-name (if (=* (get "method") "call")
+ (proc-ptr-type ttype)
+ (string-upcase! (string-append PFX "_TR_" ttype)) ))
+ (set! next (if (exist? "next") (get-up "next") "${f}"))
+
+ (shellf
+ "for f in ${st_list} ; do F=${f}
+ eval FSM_TRANS_${f}_%s=\"'{ %s_ST_%s, %s }'\"
+ done"
+ tev PFX next tr-name ) =][=
+
+ ELIF (== (get "tev") "*") =][=
+
+ ;; This transition applies for all transitions in a certain state
+ ;;
+ (set! tst (get-up "tst"))
+ (set! ttype (if (exist? "ttype")
+ (get-up "ttype") (string-append tst "_${f}") ))
+
+ (set! tr-name (if (=* (get "method") "call")
+ (proc-ptr-type ttype)
+ (string-append PFX "_TR_" ttype) ))
+
+ (set! next (if (exist? "next") (get-up "next") tst))
+
+ (shellf
+ "for f in ${ev_list} ; do
+ eval FSM_TRANS_%s_${f}=\"'{ %s_ST_%s, %s }'\"
+ done"
+ tst PFX next tr-name) =][=
+
+ ELSE =][=
+
+ FOR tst =][=
+
+ (set! tst (get-up "tst"))
+ (set! next (if (exist? "next") (get-up "next") tst))
+
+ =][=
+ FOR tev =][=
+
+ (set! tev (get-up "tev"))
+ (set! ttype (string-downcase! (if (exist? "ttype") (get "ttype")
+ (string-append tst "_" tev) )))
+
+ (set! tr-name (if (=* (get "method") "call")
+ (proc-ptr-type ttype)
+ (string-upcase! (string-append PFX "_TR_" ttype)) ))
+
+ (shellf "FSM_TRANS_%s_%s=\"{ %s_ST_%s, %s }\""
+ tst tev PFX next tr-name) =][=
+
+ ENDFOR tev =][=
+ ENDFOR tst =][=
+ ENDIF tst or ttkn as '*' =][=
+ENDFOR transition =][=
+
+(define trans-ct
+ (shellf
+ "env | egrep '^FSM_TRANS_' | \
+ sed '/, NULL }/d;s/^.*%s//;s/ .*$/,/' | \
+ sort -u > ${tmp_dir}/xlist
+ echo `wc -l < ${tmp_dir}/xlist` "
+ (if (=* (get "method") "call")
+ (string-append pfx "_do_")
+ (string-append PFX "_TR_"))
+) ) =][=
+
+ENDDEF compute-transitions
+
+# end of agen5/fsm-macro.tlib =]
diff --git a/agen5/fsm-trans.tlib b/agen5/fsm-trans.tlib
new file mode 100644
index 0000000..0706910
--- /dev/null
+++ b/agen5/fsm-trans.tlib
@@ -0,0 +1,359 @@
+[= AutoGen5 Template -*- Mode: Text -*-
+
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=][=
+(make-tmp-dir)
+
+(define get-up (lambda (nm)
+ (string-upcase! (string->c-name! (get nm))) ))
+
+(define get-down (lambda (nm)
+ (string-downcase! (string->c-name! (get nm))) ))
+
+(define stack-up (lambda (nm)
+ (string-upcase! (string->c-name! (join "\n" (stack nm)))) ))
+
+(define event-string "") =][=
+
+DEFINE state-table =]
+
+ /* STATE [= (get "st_ix") =]: [=
+ (define STATE-NAME (get-up "state"))
+ (shellf "state=%s" STATE-NAME)
+ (string-append PFX "_ST_" STATE-NAME) =] */
+ { [=
+
+ FOR event "\n " =][=
+ (set! event-string (get-up "event"))
+ (set! fmt (shellf "echo ${FSM_TRANS_%s_%s}%s"
+ STATE-NAME event-string (if (last-for?) "" ",") ))
+ (if (exist? event-string) (set! event-string (get event-string)))
+ (sprintf "%-47s /* EVT: %s */" fmt event-string ) =][=
+
+ ENDFOR
+
+=]
+ }[=
+
+ENDDEF state-table
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE enumerate-transitions =]
+/*
+ * Enumeration of the valid transition types
+ * Some transition types may be common to several transitions.
+ */
+typedef enum {
+[=(string-upcase! (shellf
+ "sed '$s/,$//;s/^/ %s_TR_/' ${tmp_dir}/xlist" PFX))=]
+} te_[=(. pfx)=]_trans;
+#define [=(. PFX)=]_TRANSITION_CT [=
+ `tct="\`wc -l < ${tmp_dir}/xlist\`"
+ echo $tct`=]
+
+/**
+ * State transition handling map. Map the state enumeration and the event
+ * enumeration to the new state and the transition enumeration code (in that
+ * order). It is indexed by first the current state and then the event code.
+ */
+typedef struct [=(. pfx)=]_transition [= (. t-trans) =];
+struct [=(. pfx)=]_transition {
+ te_[=(. pfx)=]_state next_state;
+ te_[=(. pfx)=]_trans transition;
+};
+[=
+
+ IF (exist? "use_ifdef")
+
+=]
+#ifndef DEFINE_FSM
+extern const [= (. t-trans) =] [=(. pfx)=]_trans_table[ [=(. PFX)
+=]_STATE_CT ][ [=(. PFX)=]_EVENT_CT ];
+
+extern int
+[=(. pfx)=]_invalid_transition( te_[=(. pfx)=]_state st, te_[=
+ (. pfx)=]_event evt );
+#else
+[=
+
+ ELSE
+
+=]static [=
+ ENDIF
+
+=]const [= (. t-trans) =]
+[=(. pfx)=]_trans_table[ [=(. PFX)
+=]_STATE_CT ][ [=(. PFX)=]_EVENT_CT ] = {[=
+ state-table
+ state = init
+ st_ix = "0" =][=
+
+ FOR state =],
+[= state-table st_ix = (+ 1 (for-index)) =][=
+ ENDFOR =]
+};[=
+
+ IF (exist? "use_ifdef") =][=
+ emit-invalid-msg =]
+#endif /* DEFINE_FSM */[=
+ ENDIF =][=
+
+ENDDEF enumerate-transitions
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE callback-transitions
+
+=]
+/**
+ * Callback routine prototype. They return the next state. Normally, that
+ * should be the value of the "maybe_next" argument.
+ */
+typedef te_[=(. pfx)=]_state ([=(. pfx)=]_callback_t)([=
+ emit-cookie-args =]
+ te_[=(. pfx)=]_state initial,
+ te_[=(. pfx)=]_state maybe_next,
+ te_[=(. pfx)=]_event trans_evt );
+
+static [=(. pfx)=]_callback_t
+[=(shellf "sed '$s/,$/;/;s/^/ %s_do_/' ${tmp_dir}/xlist" pfx)=]
+
+/**
+ * Declare all the state transition handling routines.
+ */
+typedef struct transition [= (. t-trans) =];
+struct transition {[=
+ (set! fmt (sprintf "\n %%-%ds %%s;"
+ (+ (string-length pfx) 14) ))
+ (sprintf (string-append fmt fmt)
+ (string-append "te_" pfx "_state") "next_state"
+ (string-append pfx "_callback_t*") "trans_proc") =]
+};
+
+/**
+ * State transition maps. Map the enumeration and the event enumeration
+ * to the new state and the transition enumeration code (in that order).
+ * It is indexed by first the current state and then the event code.
+ */
+static const [= (. t-trans) =]
+[=(. pfx)=]_trans_table[ [=(. PFX)
+=]_STATE_CT ][ [=(. PFX)=]_EVENT_CT ] = {[=
+
+ state-table
+ state = init st_ix = "0" =][=
+
+ FOR state =],[=
+ state-table st_ix = (+ 1 (for-index)) =][=
+ ENDFOR =]
+};[=
+
+ENDDEF callback-transitions
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE machine-step =][=
+
+(define trans-name "trans")
+(define trans-field "transition")
+(define trans-valu (string-append PFX "_TR_INVALID"))
+
+(if (not (=* (get "method") "case")) (begin
+ (set! trans-name "pT ")
+ (set! trans-field "trans_proc")
+ (set! trans-valu (string-append pfx "_do_invalid"))
+) )
+
+=]
+#ifndef __COVERITY__
+ if (trans_evt >= [=(. PFX)=]_EV_INVALID) {
+ nxtSt = [=(. PFX)=]_ST_INVALID;
+ [=(. trans-name)=] = [=(. trans-valu)=];
+ } else
+#endif /* __COVERITY__ */
+ {
+ const [= (. t-trans) =]* pTT =
+ [=(. pfx)=]_trans_table[ [=(. pfx)=]_state ] + trans_evt;
+[= IF (exist? "debug-flag") \=]
+#ifdef [= (get "debug-flag") =]
+ firstNext = /* next line */
+#endif /* [= "debug-flag =] */
+[= ENDIF \=]
+ nxtSt = pTT->next_state;
+ [=(. trans-name)=] = pTT->[=(. trans-field)=];
+ }
+[= IF (exist? "debug-flag") \=]
+#ifdef [= (get "debug-flag") =]
+ printf( "in state %s(%d) step %s(%d) to %s(%d)\n",
+ [=(. PFX)=]_STATE_NAME( [=(. pfx)=]_state ), [=(. pfx)=]_state,
+ [=(. PFX)=]_EVT_NAME( trans_evt ), trans_evt,
+ [=(. PFX)=]_STATE_NAME( nxtSt ), nxtSt );
+#endif /* [= "debug-flag =] */
+[= ENDIF \=][=
+
+ IF (not (=* (get "method") "case")) =][=
+ INVOKE run-callback =][=
+ ELIF (exist? "handler-file") =]
+#define FSM_SWITCH_CODE
+#include "[= handler-file =]"
+#undef FSM_SWITCH_CODE[=
+ ELSE =][=
+ INVOKE run-switch =][=
+ ENDIF
+
+=]
+[= IF (exist? "debug-flag") \=]
+#ifdef [= (get "debug-flag") =]
+ if (nxtSt != firstNext)
+ printf( "transition code changed destination state to %s(%d)\n",
+ [=(. PFX)=]_STATE_NAME( nxtSt ), nxtSt );
+#endif /* [= "debug-flag =] */
+[= ENDIF \=][=
+
+ IF (not (=* (get "type") "reent")) =]
+ [=(. pfx)=]_state = nxtSt;[=
+ ENDIF =]
+[=
+ENDDEF machine-step
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE fsm-proc-variables
+
+ =]
+[= IF (exist? "debug-flag") \=]
+#ifdef [= (get "debug-flag") =]
+ te_[=(. pfx)=]_state firstNext;
+#endif
+[= ENDIF \=]
+ te_[=(. pfx)=]_state nxtSt;[=
+ IF (=* (get "method") "call") =]
+ [=(. pfx)=]_callback_t* pT;[=
+ ELSE =]
+ te_[=(. pfx)=]_trans trans;[=
+ ENDIF =][=
+
+ENDDEF fsm-proc-variables
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE make-loop-proc =]
+/**
+ * Run the FSM. Will return [=(. PFX)=]_ST_DONE or [=(. PFX)=]_ST_INVALID
+ */
+[=mode=]te_[=(. pfx)=]_state
+[=(. pfx)=]_run_fsm([=
+ IF (exist? "cookie") =][=
+ FOR cookie "," =]
+ [=cookie=][=
+ ENDFOR=][=
+ ELSE=] void[=ENDIF=] )[=
+
+ENDDEF make-loop-proc
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE looping-machine
+
+ =][= make-loop-proc mode = "" =]
+{
+ te_[=(. pfx)=]_state [=(. pfx)=]_state = [=(. PFX)=]_ST_INIT;
+ te_[=(. pfx)=]_event trans_evt;[=
+ INVOKE fsm-proc-variables =][=
+ IF (exist? "cookie") =]
+[= (shell (string-append
+ "cookies='" (stack-join "\n" "cookie") "'
+ echo \"$cookies\" | \
+ sed -e 's/^/ /' \
+ -e 's@\\([a-zA-Z0-9_]*\\)*$@saved_\\1 = \\1;@'
+ echo \"$cookies\" | \
+ sed -e 's@.*[^a-z0-9_A-Z]\\([a-zA-Z0-9_]*\\)*$@\\1;@' \
+ -e 's/^/ (void)saved_/'" ))
+
+ =][=
+ ENDIF=]
+
+ while ([=(. pfx)=]_state < [=(. PFX)=]_ST_INVALID) {
+[=IF (exist? "handler-file")=]
+#define FSM_FIND_TRANSITION
+#include "[= handler-file =]"
+#undef FSM_FIND_TRANSITION[=
+ ELSE =]
+[=(extract fsm-source " /* %s == FIND TRANSITION == %s */" ""
+ " trans_evt = GET_NEXT_TRANS();" ) =][=
+ ENDIF =]
+[= (out-push-new (string-append tmp-dir "/cktbl"))=][=
+ INVOKE machine-step =][=
+ (out-pop) (shell
+ "sed 's/^ / /;s/ / /' ${tmp_dir}/cktbl")
+=]
+ }
+ return [=(. pfx)=]_state;
+}[=
+
+ENDDEF looping-machine
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE make-step-proc =]
+/**
+ * Step the FSM. Returns the resulting state. If the current state is
+ * [=(. PFX)=]_ST_DONE or [=(. PFX)=]_ST_INVALID, it resets to
+ * [=(. PFX)=]_ST_INIT and returns [=(. PFX)=]_ST_INIT.
+ */
+[=mode=]te_[=(. pfx)=]_state
+[=(. pfx)=]_step([=
+ IF (=* (get "type") "reent") =]
+ te_[= (. pfx) =]_state [= (. pfx) =]_state,[=
+ ENDIF =]
+ te_[= (. pfx) =]_event trans_evt[=
+ FOR cookie =],
+ [=cookie=][=
+ ENDFOR=] )[=
+
+ENDDEF make-step-proc
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=
+
+DEFINE stepping-machine
+
+ =][= INVOKE make-step-proc mode = "" =]
+{[=
+ fsm-proc-variables =]
+
+ if ((unsigned)[=(. pfx)=]_state >= [=(. PFX)=]_ST_INVALID) {[=
+ IF (=* (get "type") "step") =]
+ [=(. pfx)=]_state = [=(. PFX)=]_ST_INIT;[=
+ ENDIF =]
+ return [=(. PFX)=]_ST_INIT;
+ }
+[=INVOKE machine-step =][=
+ IF (exist? "handler-file")=]
+#define FSM_FINISH_STEP
+#include "[= handler-file =]"
+#undef FSM_FINISH_STEP[=
+ ELSE =]
+[=(extract fsm-source " /* %s == FINISH STEP == %s */")=][=
+ ENDIF =]
+
+ return nxtSt;
+}[=
+
+ENDDEF stepping-machine
+
+# end of agen5/fsm-trans.tlib =]
diff --git a/agen5/fsm.tpl b/agen5/fsm.tpl
new file mode 100644
index 0000000..8039776
--- /dev/null
+++ b/agen5/fsm.tpl
@@ -0,0 +1,207 @@
+[= AutoGen5 Template -*- Mode: text -*-
+
+h=%s-fsm.h
+
+c=%s-fsm.c
+
+# Time-stamp: "2011-06-03 12:10:58 bkorb"
+
+## This file is part of AutoGen.
+## Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+## NB: THIS FILE IS GPL. THE OUTPUT OF THIS FILE IS LICENSED MBSD.
+
+(setenv "SHELL" "/bin/sh")
+
+=]
+[=
+
+CASE (suffix) =][=
+
+== h =][=
+
+ (define fmt "")
+ (define fsm-ver "0.1") =][=
+ INCLUDE "fsm-trans.tlib" =][=
+ INCLUDE "fsm-macro.tlib" =][=
+
+ INVOKE preamble
+
+=]
+/*
+ * This file enumerates the states and transition events for a FSM.
+ *
+ * te_[=(. pfx)=]_state
+ * The available states. FSS_INIT is always defined to be zero
+ * and FSS_INVALID and FSS_DONE are always made the last entries.
+ *
+ * te_[=(. pfx)=]_event
+ * The transition events. These enumerate the event values used
+ * to select the next state from the current state.
+ * [=(. PFX)=]_EV_INVALID is always defined at the end.
+ */
+[=(make-header-guard "autofsm")=]
+[=
+
+FOR extra-header "\n" \=]
+#include "[=extra-header=]"[=
+ENDFOR
+
+=]
+/*
+ * Finite State machine States
+ *
+ * Count of non-terminal states. The generated states INVALID and DONE
+ * are terminal, but INIT is not :-).
+ */
+#define [=(. PFX)=]_STATE_CT [=(+ 1 (count "state"))=]
+typedef enum {
+[=
+ (shell (string-append
+ "${CLexe-columns} --spread=1 -I4 -S, -f'" PFX "_ST_%s' <<_EOF_
+INIT
+" (stack-up "state") "
+INVALID
+DONE
+_EOF_" )) =]
+} te_[=(. pfx)=]_state;
+
+/*
+ * Finite State machine transition Events.
+ *
+ * Count of the valid transition events
+ */
+#define [=(. PFX)=]_EVENT_CT [=(count "event")=]
+typedef enum {
+[= compute-transitions =][=
+ (shellf "${CLexe-columns} --spread=1 -I4 -S, -f'%s_EV_%%s' <<_EOF_
+%s
+INVALID
+_EOF_" PFX (stack-up "event") )=]
+} te_[=(. pfx)=]_event;
+[=
+
+ CASE method =][=
+
+ ~* call|case =][=
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+ #
+ # We are implementing the machine. Declare the external =][=
+
+ CASE type =][=
+
+ ~* step|reent =][= make-step-proc mode = "extern " =];[=
+
+ =* loop =][= make-loop-proc mode = "extern " =];[=
+
+ * =][=
+ (error (string-append "invalid FSM type: ``" (get "type")
+ "'' must be ``looping'', ``stepping'' or ``reentrant''" ))
+ =][=
+ ESAC =][=
+
+ # End external procedure declarations
+ #
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+ #
+ # We are *NOT* implementing the machine. Define the table =][=
+
+ == "" =][=
+ enumerate-transitions use_ifdef = yes =][=
+ =* no =][=
+ enumerate-transitions use_ifdef = yes =][=
+ * =][=
+ (error (sprintf
+ "invalid FSM method: ``%s'' must be ``callout'', ``case'' or ``none''"
+ (get "method"))) =][=
+ ESAC =]
+
+#endif /* [=(. header-guard)=] */[=
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+#
+# C OUTPUT BEGINS HERE
+#
+=][=
+
+== c =][=
+
+ (if (~ (get "method") "(no.*){0,1}")
+ (out-delete) ) =][=
+
+ INVOKE preamble
+
+=]
+#define DEFINE_FSM
+#include "[=(. header-file)=]"
+#include <stdio.h>
+[=IF (exist? "handler-file")=]
+#define FSM_USER_HEADERS
+#include "[= handler-file =]"
+#undef FSM_USER_HEADERS[=
+ ELSE =]
+/*
+ * Do not make changes to this file, except between the START/END
+ * comments, or it will be removed the next time it is generated.
+ */
+[=(extract fsm-source "/* %s === USER HEADERS === %s */")=][=
+ ENDIF =]
+
+#ifndef NULL
+# define NULL 0
+#endif
+[= CASE method =][=
+ =* "case" =][= enumerate-transitions =][=
+ =* "call" =][= callback-transitions =][=
+ ESAC =]
+[=IF (=* (get "type") "step")=]
+/*
+ * The FSM machine state
+ */
+static te_[=(. pfx)=]_state [=(. pfx)=]_state = [=(. PFX)=]_ST_INIT;
+[=ENDIF=]
+[= emit-invalid-msg =][=
+
+ IF (=* (get "method") "call") =][=
+
+ IF (exist? "handler-file") =]
+#define FSM_HANDLER_CODE
+#include "[= handler-file =]"
+#undef FSM_HANDLER_CODE
+[=
+ ELSE =][=
+ INVOKE callbacks =][=
+ ENDIF =][=
+
+ ELSE =][=
+ ENDIF =][=
+
+ CASE type =][=
+ =* loop =][= looping-machine =][=
+ ~* step|reent =][= stepping-machine =][=
+ ESAC =][=
+
+ESAC (suffix)
+
+=]
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of [= (out-name) ;; agen5/fsm.tpl =] */
diff --git a/agen5/funcCase.c b/agen5/funcCase.c
new file mode 100644
index 0000000..b725d28
--- /dev/null
+++ b/agen5/funcCase.c
@@ -0,0 +1,1390 @@
+
+/**
+ * @file funcCase.c
+ *
+ * This module implements the CASE text function.
+ *
+ * Time-stamp: "2012-04-28 08:04:06 bkorb"
+ */
+/*
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#undef IS_LOW
+#define IS_LOW(c) (((c) <= 'z') && ((c) >= 'a'))
+
+#ifndef _toupper
+# ifdef __toupper
+# define _toupper(c) __toupper(c)
+# else
+# define _toupper(c) toupper(c)
+# endif
+#endif
+
+#define PTRUP(p) STMTS(if(IS_LOW(*(p))) *(p)=_toupper(*(p));(p)++)
+
+typedef tSuccess (tSelectProc)(char const * sample, char const * pattern);
+static tSelectProc
+ Select_Compare,
+ Select_Compare_End,
+ Select_Compare_Start,
+ Select_Compare_Full,
+ Select_Equivalent,
+ Select_Equivalent_End,
+ Select_Equivalent_Start,
+ Select_Equivalent_Full,
+ Select_Match,
+ Select_Match_End,
+ Select_Match_Start,
+ Select_Match_Full,
+ Select_Match_Always;
+
+/*
+ * This is global data used to keep track of the current CASE
+ * statement being processed. When CASE statements nest,
+ * these data are copied onto the stack and restored when
+ * the nested CASE statement's ESAC function is found.
+ */
+typedef struct case_stack tCaseStack;
+struct case_stack {
+ macro_t* pCase;
+ macro_t* pSelect;
+};
+
+static tCaseStack current_case;
+static load_proc_t mLoad_Select;
+
+static load_proc_p_t apCaseLoad[ FUNC_CT ] = { NULL };
+static load_proc_p_t apSelectOnly[ FUNC_CT ] = { NULL };
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+compile_re(regex_t* pRe, char const * pzPat, int flags);
+
+static inline void
+up_case(char* pz);
+
+static tSuccess
+Select_Compare(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Compare_End(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Compare_Start(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Compare_Full(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Equivalent(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Equivalent_End(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Equivalent_Start(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Equivalent_Full(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Match(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Match_End(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Match_Start(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Match_Full(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Match_Always(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Match_Existence(char const * sample, char const * pattern);
+
+static tSuccess
+Select_Match_NonExistence(char const * sample, char const * pattern);
+
+static bool
+selection_type_complete(templ_t * tpl, macro_t * mac, char const ** psrc);
+
+static macro_t *
+mLoad_Select(templ_t * tpl, macro_t * mac, char const ** pscan);
+/* = = = END-STATIC-FORWARD = = = */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void
+compile_re(regex_t* pRe, char const * pzPat, int flags)
+{
+ void * const pat = (void *)pzPat;
+ int rerr = regcomp(pRe, pat, flags);
+ if (rerr != 0) {
+ char zEr[ SCRIBBLE_SIZE ];
+ regerror(rerr, pRe, zEr, sizeof(zEr));
+ fprintf(stderr, BAD_RE_FMT, rerr, zEr, pzPat);
+ AG_ABEND(COMPILE_RE_BAD);
+ }
+}
+
+
+static inline void
+up_case(char* pz)
+{
+ while (*pz != NUL) PTRUP(pz);
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_contains_p
+ *
+ * what: substring match
+ * general_use:
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*==*"
+ *
+ * doc: Test to see if a string contains a substring. "strstr(3)"
+ * will find an address.
+=*/
+static tSuccess
+Select_Compare(char const * sample, char const * pattern)
+{
+ return (strstr(sample, pattern)) ? SUCCESS : FAILURE;
+}
+
+SCM
+ag_scm_string_contains_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+
+ return (strstr(pzText, pzSubstr) == NULL) ? SCM_BOOL_F : SCM_BOOL_T;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_ends_with_p
+ *
+ * what: string ending
+ * general_use:
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*=="
+ *
+ * doc: Test to see if a string ends with a substring.
+ * strcmp(3) returns zero for comparing the string ends.
+=*/
+static tSuccess
+Select_Compare_End(char const * sample, char const * pattern)
+{
+ size_t vlen = strlen(pattern);
+ size_t tlen = strlen(sample);
+ tSuccess res;
+
+ if (tlen < vlen)
+ res = FAILURE;
+ else if (strcmp(sample + (tlen - vlen), pattern) == 0)
+ res = SUCCESS;
+ else res = FAILURE;
+
+ return res;
+}
+
+SCM
+ag_scm_string_ends_with_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ return (SUCCESSFUL(Select_Compare_End(pzText, pzSubstr)))
+ ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_starts_with_p
+ *
+ * what: string starting
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "==*"
+ *
+ * doc: Test to see if a string starts with a substring.
+=*/
+static tSuccess
+Select_Compare_Start(char const * sample, char const * pattern)
+{
+ size_t vlen = strlen(pattern);
+ tSuccess res;
+
+ if (strncmp(sample, pattern, vlen) == 0)
+ res = SUCCESS;
+ else res = FAILURE;
+
+ return res;
+}
+
+SCM
+ag_scm_string_starts_with_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ return (SUCCESSFUL(Select_Compare_Start(pzText, pzSubstr)))
+ ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_equals_p
+ *
+ * what: string matching
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "=="
+ *
+ * doc: Test to see if two strings exactly match.
+=*/
+static tSuccess
+Select_Compare_Full(char const * sample, char const * pattern)
+{
+ return (strcmp(sample, pattern) == 0) ? SUCCESS : FAILURE;
+}
+
+SCM
+ag_scm_string_equals_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+
+ return (strcmp(pzText, pzSubstr) == 0) ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_contains_eqv_p
+ *
+ * what: caseless substring
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*=*"
+ *
+ * doc: Test to see if a string contains an equivalent string.
+ * `equivalent' means the strings match, but without regard
+ * to character case and certain characters are considered `equivalent'.
+ * Viz., '-', '_' and '^' are equivalent.
+=*/
+static tSuccess
+Select_Equivalent(char const * sample, char const * pattern)
+{
+ char* pz;
+ tSuccess res = SUCCESS;
+ AGDUPSTR(pz, sample, "equiv chars");
+ up_case(pz);
+ if (strstr(pz, pattern) == NULL)
+ res = FAILURE;
+ AGFREE((void*)pz);
+
+ return res;
+}
+
+SCM
+ag_scm_string_contains_eqv_p(SCM text, SCM substr)
+{
+ char * pzSubstr;
+ SCM res;
+
+ AGDUPSTR(pzSubstr, ag_scm2zchars(substr, "search"), "substr");
+
+ up_case(pzSubstr);
+ if (SUCCESSFUL(Select_Equivalent(ag_scm2zchars(text, "sample"),
+ pzSubstr)))
+ res = SCM_BOOL_T;
+ else res = SCM_BOOL_F;
+ AGFREE((void*)pzSubstr);
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_ends_eqv_p
+ *
+ * what: caseless string ending
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*="
+ *
+ * doc: Test to see if a string ends with an equivalent string.
+=*/
+static tSuccess
+Select_Equivalent_End(char const * sample, char const * pattern)
+{
+ size_t vlen = strlen(pattern);
+ size_t tlen = strlen(sample);
+
+ if (tlen < vlen)
+ return FAILURE;
+
+ return (streqvcmp(sample + (tlen - vlen), pattern) == 0)
+ ? SUCCESS
+ : FAILURE;
+}
+
+SCM
+ag_scm_string_ends_eqv_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ return (SUCCESSFUL(Select_Equivalent_End( pzText, pzSubstr )))
+ ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_starts_eqv_p
+ *
+ * what: caseless string start
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "=*"
+ *
+ * doc: Test to see if a string starts with an equivalent string.
+=*/
+static tSuccess
+Select_Equivalent_Start(char const * sample, char const * pattern)
+{
+ size_t vlen = strlen(pattern);
+
+ return (strneqvcmp(sample, pattern, (int)vlen) == 0)
+ ? SUCCESS
+ : FAILURE;
+}
+
+SCM
+ag_scm_string_starts_eqv_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ return (SUCCESSFUL(Select_Equivalent_Start(pzText, pzSubstr)))
+ ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_eqv_p
+ *
+ * what: caseless match
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "="
+ *
+ * doc: Test to see if two strings are equivalent. `equivalent' means the
+ * strings match, but without regard to character case and certain
+ * characters are considered `equivalent'. Viz., '-', '_' and '^' are
+ * equivalent. If the arguments are not strings, then the result of the
+ * numeric comparison is returned.
+ *
+ * This is an overloaded operation. If the arguments are both
+ * numbers, then the query is passed through to @code{scm_num_eq_p()},
+ * otherwise the result depends on the SCMs being strictly equal.
+=*/
+static tSuccess
+Select_Equivalent_Full(char const * sample, char const * pattern)
+{
+ return (streqvcmp(sample, pattern) == 0) ? SUCCESS : FAILURE;
+}
+
+SCM
+ag_scm_string_eqv_p(SCM text, SCM substr)
+{
+ /*
+ * We are overloading the "=" operator. Our arguments may be
+ * numbers or booleans...
+ */
+ teGuileType tt = ag_scm_type_e(text);
+ {
+ teGuileType st = ag_scm_type_e(substr);
+ if (st != tt)
+ return SCM_BOOL_F;
+ }
+
+ switch (tt) {
+ case GH_TYPE_NUMBER:
+ return scm_num_eq_p(text, substr);
+
+ case GH_TYPE_STRING:
+ {
+ char * pzText = ag_scm2zchars(text, "text");
+ char * pzSubstr = ag_scm2zchars(substr, "m expr");
+ return (streqvcmp(pzText, pzSubstr) == 0) ? SCM_BOOL_T : SCM_BOOL_F;
+ }
+
+ case GH_TYPE_BOOLEAN:
+ default:
+ return (text == substr) ? SCM_BOOL_T : SCM_BOOL_F;
+ }
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_has_match_p
+ *
+ * what: contained regex match
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*~~*"
+ *
+ * doc: Test to see if a string contains a pattern.
+ * Case is significant.
+=*/
+/*=gfunc string_has_eqv_match_p
+ *
+ * what: caseless regex contains
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*~*"
+ *
+ * doc: Test to see if a string contains a pattern.
+ * Case is not significant.
+=*/
+static tSuccess
+Select_Match(char const * sample, char const * pattern)
+{
+ /*
+ * On the first call for this macro, compile the expression
+ */
+ if (cur_macro->md_pvt == NULL) {
+ void * mat = (void *)pattern;
+ regex_t* pRe = AGALOC(sizeof(*pRe), "select match re");
+ compile_re(pRe, mat, (int)cur_macro->md_res);
+ cur_macro->md_pvt = (void*)pRe;
+ }
+
+ if (regexec((regex_t*)cur_macro->md_pvt, sample, (size_t)0,
+ NULL, 0) != 0)
+ return FAILURE;
+ return SUCCESS;
+}
+
+SCM
+ag_scm_string_has_match_p(SCM text, SCM substr)
+{
+ SCM res;
+ regex_t re;
+
+ compile_re(&re, ag_scm2zchars( substr, "match expr" ), REG_EXTENDED);
+
+ if (regexec(&re, ag_scm2zchars(text, "text to match"), (size_t)0,
+ NULL, 0) == 0)
+ res = SCM_BOOL_T;
+ else res = SCM_BOOL_F;
+ regfree(&re);
+
+ return res;
+}
+
+SCM
+ag_scm_string_has_eqv_match_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ SCM res;
+ regex_t re;
+
+ compile_re(&re, pzSubstr, REG_EXTENDED | REG_ICASE);
+
+ if (regexec(&re, pzText, (size_t)0, NULL, 0) == 0)
+ res = SCM_BOOL_T;
+ else res = SCM_BOOL_F;
+ regfree(&re);
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_end_match_p
+ *
+ * what: regex match end
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*~~"
+ *
+ * doc: Test to see if a string ends with a pattern.
+ * Case is significant.
+=*/
+/*=gfunc string_end_eqv_match_p
+ *
+ * what: caseless regex ending
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "*~"
+ *
+ * doc: Test to see if a string ends with a pattern.
+ * Case is not significant.
+=*/
+static tSuccess
+Select_Match_End(char const * sample, char const * pattern)
+{
+ regmatch_t m[2];
+ /*
+ * On the first call for this macro, compile the expression
+ */
+ if (cur_macro->md_pvt == NULL) {
+ void * mat = (void *)pattern;
+ regex_t* pRe = AGALOC(sizeof(*pRe), "select match end re");
+ compile_re(pRe, mat, (int)cur_macro->md_res);
+ cur_macro->md_pvt = (void*)pRe;
+ }
+
+ if (regexec((regex_t*)cur_macro->md_pvt, sample, (size_t)2, m, 0)
+ != 0)
+ return FAILURE;
+ if (m[0].rm_eo != (int)strlen(sample))
+ return FAILURE;
+ return SUCCESS;
+}
+
+SCM
+ag_scm_string_end_match_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ SCM res;
+ regex_t re;
+ regmatch_t m[2];
+
+ compile_re(&re, pzSubstr, REG_EXTENDED);
+
+ if (regexec(&re, pzText, (size_t)2, m, 0) != 0)
+ res = SCM_BOOL_F;
+ else if (m[0].rm_eo != (int)strlen(pzText))
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ regfree(&re);
+
+ return res;
+}
+
+SCM
+ag_scm_string_end_eqv_match_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ SCM res;
+ regex_t re;
+ regmatch_t m[2];
+
+ compile_re(&re, pzSubstr, REG_EXTENDED | REG_ICASE);
+
+ if (regexec(&re, pzText, (size_t)2, m, 0) != 0)
+ res = SCM_BOOL_F;
+ else if (m[0].rm_eo != (int)strlen(pzText))
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ regfree(&re);
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_start_match_p
+ *
+ * what: regex match start
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "~~*"
+ *
+ * doc: Test to see if a string starts with a pattern.
+ * Case is significant.
+=*/
+/*=gfunc string_start_eqv_match_p
+ *
+ * what: caseless regex start
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "~*"
+ *
+ * doc: Test to see if a string starts with a pattern.
+ * Case is not significant.
+=*/
+static tSuccess
+Select_Match_Start(char const * sample, char const * pattern)
+{
+ regmatch_t m[2];
+ /*
+ * On the first call for this macro, compile the expression
+ */
+ if (cur_macro->md_pvt == NULL) {
+ void * mat = (void *)pattern;
+ regex_t* pRe = AGALOC(sizeof(*pRe), "select match start re");
+ compile_re(pRe, mat, (int)cur_macro->md_res);
+ cur_macro->md_pvt = (void*)pRe;
+ }
+
+ if (regexec((regex_t*)cur_macro->md_pvt, sample, (size_t)2, m, 0)
+ != 0)
+ return FAILURE;
+ if (m[0].rm_so != 0)
+ return FAILURE;
+ return SUCCESS;
+}
+
+SCM
+ag_scm_string_start_match_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ SCM res;
+ regex_t re;
+ regmatch_t m[2];
+
+ compile_re(&re, pzSubstr, REG_EXTENDED);
+
+ if (regexec(&re, pzText, (size_t)2, m, 0) != 0)
+ res = SCM_BOOL_F;
+ else if (m[0].rm_so != 0)
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ regfree(&re);
+
+ return res;
+}
+
+SCM
+ag_scm_string_start_eqv_match_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ SCM res;
+ regex_t re;
+ regmatch_t m[2];
+
+ compile_re(&re, pzSubstr, REG_EXTENDED | REG_ICASE);
+
+ if (regexec(&re, pzText, (size_t)2, m, 0) != 0)
+ res = SCM_BOOL_F;
+ else if (m[0].rm_so != 0)
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ regfree(&re);
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=gfunc string_match_p
+ *
+ * what: regex match
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "~~"
+ *
+ * doc: Test to see if a string fully matches a pattern.
+ * Case is significant.
+=*/
+/*=gfunc string_eqv_match_p
+ *
+ * what: caseless regex match
+ * general_use:
+ *
+ * exparg: text, text to test for pattern
+ * exparg: match, pattern/substring to search for
+ *
+ * string: "~"
+ *
+ * doc: Test to see if a string fully matches a pattern.
+ * Case is not significant, but any character equivalences
+ * must be expressed in your regular expression.
+=*/
+static tSuccess
+Select_Match_Full(char const * sample, char const * pattern)
+{
+ regmatch_t m[2];
+
+ /*
+ * On the first call for this macro, compile the expression
+ */
+ if (cur_macro->md_pvt == NULL) {
+ void * mat = (void *)pattern;
+ regex_t* pRe = AGALOC(sizeof(*pRe), "select match full re");
+
+ if (OPT_VALUE_TRACE > TRACE_EXPRESSIONS) {
+ fprintf(trace_fp, TRACE_SEL_MATCH_FULL,
+ pattern, cur_macro->md_res);
+ }
+ compile_re(pRe, mat, (int)cur_macro->md_res);
+ cur_macro->md_pvt = pRe;
+ }
+
+ if (regexec((regex_t*)cur_macro->md_pvt, sample, (size_t)2, m, 0)
+ != 0)
+ return FAILURE;
+
+ if ( (m[0].rm_eo != (int)strlen( sample ))
+ || (m[0].rm_so != 0))
+ return FAILURE;
+ return SUCCESS;
+}
+
+SCM
+ag_scm_string_match_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ SCM res;
+ regex_t re;
+ regmatch_t m[2];
+
+ compile_re(&re, pzSubstr, REG_EXTENDED);
+
+ if (regexec(&re, pzText, (size_t)2, m, 0) != 0)
+ res = SCM_BOOL_F;
+ else if ( (m[0].rm_eo != (int)strlen(pzText))
+ || (m[0].rm_so != 0) )
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ regfree(&re);
+
+ return res;
+}
+
+SCM
+ag_scm_string_eqv_match_p(SCM text, SCM substr)
+{
+ char* pzText = ag_scm2zchars(text, "text to match");
+ char* pzSubstr = ag_scm2zchars(substr, "match expr");
+ SCM res;
+ regex_t re;
+ regmatch_t m[2];
+
+ compile_re(&re, pzSubstr, REG_EXTENDED | REG_ICASE);
+
+ if (regexec(&re, pzText, (size_t)2, m, 0) != 0)
+ res = SCM_BOOL_F;
+ else if ( (m[0].rm_eo != (int)strlen(pzText))
+ || (m[0].rm_so != 0) )
+ res = SCM_BOOL_F;
+ else res = SCM_BOOL_T;
+
+ regfree(&re);
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/**
+ * We don't bother making a Guile function for any of these :)
+ */
+static tSuccess
+Select_Match_Always(char const * sample, char const * pattern)
+{
+ (void)sample;
+ (void)pattern;
+ return SUCCESS;
+}
+
+/**
+ * If the "sample" addresses "zNil", then we couldn't find a value and
+ * defaulted to an empty string. So, the result is true if the sample
+ * address is anything except "zNil".
+ */
+static tSuccess
+Select_Match_Existence(char const * sample, char const * pattern)
+{
+ (void)pattern;
+ return (sample != no_def_str) ? SUCCESS : FAILURE;
+}
+
+/**
+ * If the "sample" addresses "zUndefined", then we couldn't find a value and
+ * defaulted to an empty string. So, the result false if the sample address
+ * is anything except "zUndefined".
+ */
+static tSuccess
+Select_Match_NonExistence(char const * sample, char const * pattern)
+{
+ (void)pattern;
+ return (sample == no_def_str) ? SUCCESS : FAILURE;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=macfunc CASE
+ *
+ * what: Select one of several template blocks
+ * handler_proc:
+ * load_proc:
+ *
+ * desc:
+ *
+ * The arguments are evaluated and converted to a string, if necessary. A
+ * simple name will be interpreted as an AutoGen value name and its value will
+ * be used by the @code{SELECT} macros (see the example below and the
+ * expression evaluation function, @pxref{EXPR}). The scope of the macro is
+ * up to the matching @code{ESAC} macro. Within the scope of a @code{CASE},
+ * this string is matched against case selection macros. There are sixteen
+ * match macros that are derived from four different ways matches may be
+ * performed, plus an "always true", "true if the AutoGen value was found",
+ * and "true if no AutoGen value was found" matches. The codes for the
+ * nineteen match macros are formed as follows:
+ *
+ * @enumerate
+ * @item
+ * Must the match start matching from the beginning of the string?
+ * If not, then the match macro code starts with an asterisk (@code{*}).
+ * @item
+ * Must the match finish matching at the end of the string?
+ * If not, then the match macro code ends with an asterisk (@code{*}).
+ * @item
+ * Is the match a pattern match or a string comparison?
+ * If a comparison, use an equal sign (@code{=}).
+ * If a pattern match, use a tilde (@code{~}).
+ * @item
+ * Is the match case sensitive?
+ * If alphabetic case is important, double the tilde or equal sign.
+ * @item
+ * Do you need a default match when none of the others match?
+ * Use a single asterisk (@code{*}).
+ * @item
+ * Do you need to distinguish between an empty string value and a value
+ * that was not found? Use the non-existence test (@code{!E}) before
+ * testing a full match against an empty string (@code{== ''}).
+ * There is also an existence test (@code{+E}), more for symmetry than
+ * for practical use.
+ * @end enumerate
+ *
+ * @noindent
+ * For example:
+ *
+ * @example
+ * [+ CASE <full-expression> +]
+ * [+ ~~* "[Tt]est" +]reg exp must match at start, not at end
+ * [+ == "TeSt" +]a full-string, case sensitive compare
+ * [+ = "TEST" +]a full-string, case insensitive compare
+ * [+ !E +]not exists - matches if no AutoGen value found
+ * [+ == "" +]expression yielded a zero-length string
+ * [+ +E +]exists - matches if there is any value result
+ * [+ * +]always match - no testing
+ * [+ ESAC +]
+ * @end example
+ *
+ * @code{<full-expression>} (@pxref{expression syntax}) may be any expression,
+ * including the use of apply-codes and value-names. If the expression yields
+ * a number, it is converted to a decimal string.
+ *
+ * These case selection codes have also been implemented as
+ * Scheme expression functions using the same codes. They are documented
+ * in this texi doc as ``string-*?'' predicates (@pxref{Common Functions}).
+=*/
+/*=macfunc ESAC
+ *
+ * what: Terminate the @code{CASE} Template Block
+ * in-context:
+ *
+ * desc:
+ * This macro ends the @code{CASE} function template block.
+ * For a complete description, @xref{CASE}.
+=*/
+macro_t *
+mFunc_Case(templ_t* pT, macro_t* pMac)
+{
+ typedef tSuccess (t_match_proc)(char const *, char const *);
+ /*
+ * There are only 15 procedures because the case insenstive matching
+ * get mapped into the previous four. The last three are "match always",
+ * "match if a value was found" "match if no value found".
+ */
+ static t_match_proc * const match_procs[] = {
+ &Select_Compare_Full,
+ &Select_Compare_End,
+ &Select_Compare_Start,
+ &Select_Compare,
+
+ &Select_Equivalent_Full,
+ &Select_Equivalent_End,
+ &Select_Equivalent_Start,
+ &Select_Equivalent,
+
+ &Select_Match_Full,
+ &Select_Match_End,
+ &Select_Match_Start,
+ &Select_Match,
+
+ &Select_Match_Always,
+ &Select_Match_Existence,
+ &Select_Match_NonExistence
+ };
+
+ static char const * const match_names[] = {
+ "COMPARE_FULL",
+ "COMPARE_END",
+ "COMPARE_START",
+ "CONTAINS",
+
+ "EQUIVALENT_FULL",
+ "EQUIVALENT_END",
+ "EQUIVALENT_START",
+ "EQUIV_CONTAINS",
+
+ "MATCH_FULL",
+ "MATCH_END",
+ "MATCH_START",
+ "MATCH_WITHIN",
+
+ "MATCH_ALWAYS",
+ "MATCH_EXISTENCE",
+ "MATCH_NONEXISTENCE"
+ };
+
+ macro_t * end_mac = pT->td_macros + pMac->md_end_idx;
+ bool free_txt;
+ char const * samp_text = eval_mac_expr(&free_txt);
+
+ /*
+ * Search through the selection clauses until we either
+ * reach the end of the list for this CASE macro, or we match.
+ */
+ for (;;) {
+ tSuccess mRes;
+ pMac = pT->td_macros + pMac->md_sib_idx;
+ if (pMac >= end_mac) {
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) {
+ fprintf(trace_fp, TRACE_CASE_FAIL, samp_text);
+
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT,
+ current_tpl->td_file, pMac->md_line);
+ }
+
+ break;
+ }
+
+ /*
+ * The current macro becomes the selected selection macro
+ */
+ cur_macro = pMac;
+ mRes = (*(match_procs[pMac->md_code & 0x0F])
+ )(samp_text, pT->td_text + pMac->md_txt_off);
+
+ /*
+ * IF match, THEN generate and stop looking for a match.
+ */
+ if (SUCCEEDED(mRes)) {
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) {
+ fprintf(trace_fp, TRACE_CASE_MATCHED,
+ samp_text,
+ match_names[pMac->md_code & 0x0F],
+ pT->td_text + pMac->md_txt_off);
+
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT,
+ current_tpl->td_file, pMac->md_line);
+ }
+
+ gen_block(pT, pMac + 1, pT->td_macros + pMac->md_sib_idx);
+ break;
+ }
+ else if (OPT_VALUE_TRACE == TRACE_EVERYTHING) {
+ fprintf(trace_fp, TRACE_CASE_NOMATCH,
+ samp_text,
+ match_names[pMac->md_code & 0x0F],
+ pT->td_text + pMac->md_txt_off);
+ }
+ }
+
+ if (free_txt)
+ AGFREE((void*)samp_text);
+
+ return end_mac;
+}
+
+/*
+ * mLoad_CASE
+ *
+ * This function is called to set up (load) the macro
+ * when the template is first read in (before processing).
+ */
+macro_t *
+mLoad_Case(templ_t* pT, macro_t* pMac, char const ** ppzScan)
+{
+ size_t srcLen = (size_t)pMac->md_res; /* macro len */
+ tCaseStack save_stack = current_case;
+ macro_t* pEsacMac;
+
+ /*
+ * Save the global macro loading mode
+ */
+ load_proc_p_t const * papLP = load_proc_table;
+
+ /*
+ * IF there is no associated text expression
+ * THEN woops! what are we to case on?
+ */
+ if (srcLen == 0)
+ AG_ABEND_IN(pT, pMac, LD_CASE_NO_EXPR);
+
+ /*
+ * Load the expression
+ */
+ (void)mLoad_Expr(pT, pMac, ppzScan);
+
+ /*
+ * IF this is the first time here,
+ * THEN set up the "CASE" mode callout tables.
+ * It is the standard table, except entries are inserted
+ * for SELECT and ESAC.
+ */
+ if (apCaseLoad[0] == NULL) {
+ int i;
+
+ /*
+ * Until there is a selection clause, only comment and select
+ * macros are allowed.
+ */
+ for (i=0; i < FUNC_CT; i++)
+ apSelectOnly[i] = mLoad_Bogus;
+
+ memcpy((void*)apCaseLoad, base_load_table, sizeof( base_load_table ));
+ apSelectOnly[ FTYP_COMMENT] = mLoad_Comment;
+ apSelectOnly[ FTYP_SELECT ] = \
+ apCaseLoad[ FTYP_SELECT ] = mLoad_Select;
+ apCaseLoad[ FTYP_ESAC ] = mLoad_Ending;
+ }
+
+ /*
+ * Set the "select macro only" loading mode
+ */
+ load_proc_table = apSelectOnly;
+
+ /*
+ * Save global pointers to the current macro entry.
+ * We will need this to link the CASE, SELECT and ESAC
+ * functions together.
+ */
+ current_case.pCase = current_case.pSelect = pMac;
+
+ /*
+ * Continue parsing the template from this nested level
+ */
+ pEsacMac = parse_tpl(pMac+1, ppzScan);
+ if (*ppzScan == NULL)
+ AG_ABEND_IN(pT, pMac, LD_CASE_NO_ESAC);
+
+ /*
+ * Tell the last select macro where its end is.
+ * (It ends with the "next" sibling. Since there
+ * is no "next" at the end, it is a tiny lie.)
+ *
+ * Also, make sure the CASE macro knows where the end is.
+ */
+ pMac->md_end_idx = \
+ current_case.pSelect->md_sib_idx = (pEsacMac - pT->td_macros);
+
+ /*
+ * Restore any enclosing CASE function's context.
+ */
+ current_case = save_stack;
+
+ /*
+ * Restore the global macro loading mode
+ */
+ load_proc_table = papLP;
+
+ /*
+ * Return the next available macro descriptor
+ */
+ return pEsacMac;
+}
+
+/**
+ * Figure out the selection type. Return "true" (it is complete) if
+ * no argument is required. That is, if it is a "match anything" or
+ * an existence/non-existence test.
+ *
+ * @param[in] tpl The active template
+ * @param[in,out] mac The selection macro structure
+ * @param[out] psrc The scan pointer for the selection argument
+ */
+static bool
+selection_type_complete(templ_t * tpl, macro_t * mac, char const ** psrc)
+{
+ char const * src = (char*)mac->md_txt_off;
+ mac_func_t fcode = FTYP_SELECT_COMPARE_FULL;
+
+ /*
+ * IF the first character is an asterisk,
+ * THEN the match can start anywhere in the string
+ */
+ if (*src == '*') {
+ src++;
+ if (IS_END_TOKEN_CHAR(*src)) {
+ mac->md_code = FTYP_SELECT_MATCH_ANYTHING;
+ goto leave_done;
+ }
+
+ fcode = (mac_func_t)(
+ (unsigned int)FTYP_SELECT_COMPARE_FULL |
+ (unsigned int)FTYP_SELECT_COMPARE_SKP_START);
+ }
+
+ /*
+ * The next character must indicate whether we are
+ * pattern matching ('~') or doing string compares ('=')
+ */
+ switch (*src++) {
+ case '~':
+ /*
+ * Or in the pattern matching bit
+ */
+ fcode = (mac_func_t)(
+ (unsigned int)fcode | (unsigned int)FTYP_SELECT_MATCH_FULL);
+ mac->md_res = REG_EXTENDED;
+ /* FALLTHROUGH */
+
+ case '=':
+ /*
+ * IF the '~' or '=' is doubled,
+ * THEN it is a case sensitive match. Skip over the char.
+ * ELSE or in the case insensitive bit
+ */
+ if (src[0] == src[-1]) {
+ src++;
+ } else {
+ fcode = (mac_func_t)(
+ (unsigned int)fcode |
+ (unsigned int)FTYP_SELECT_EQUIVALENT_FULL);
+ }
+ break;
+
+ case '!':
+ case '+':
+ switch (*src) {
+ case 'e':
+ case 'E':
+ break;
+ default:
+ goto bad_sel;
+ }
+ if (! IS_END_TOKEN_CHAR(src[1]))
+ goto bad_sel;
+
+ mac->md_code = (src[-1] == '!')
+ ? FTYP_SELECT_MATCH_NONEXISTENCE
+ : FTYP_SELECT_MATCH_EXISTENCE;
+
+ goto leave_done;
+
+ default:
+ bad_sel:
+ AG_ABEND_IN(tpl, mac, LD_SEL_INVAL);
+ }
+
+ /*
+ * IF the last character is an asterisk,
+ * THEN the match may end before the test string ends.
+ * OR in the "may end early" bit.
+ */
+ if (*src == '*') {
+ src++;
+ fcode = (mac_func_t)(
+ (unsigned int)fcode |
+ (unsigned int)FTYP_SELECT_COMPARE_SKP_END);
+ }
+
+ if (! IS_END_TOKEN_CHAR(*src))
+ AG_ABEND_IN(tpl, mac, LD_SEL_INVAL);
+
+ mac->md_code = fcode;
+ *psrc = SPN_WHITESPACE_CHARS(src);
+ return false;
+
+ leave_done:
+
+ /*
+ * md_code has been set. Zero out md_txt_off to indicate
+ * no argument text. NULL the selection argument pointer.
+ */
+ mac->md_txt_off = 0;
+ *psrc = NULL;
+ return true;
+}
+
+/*=macfunc SELECT
+ *
+ * what: Selection block for CASE function
+ * in-context:
+ * alias: | ~ | = | * | ! | + |
+ * unload-proc:
+ *
+ * desc:
+ * This macro selects a block of text by matching an expression
+ * against the sample text expression evaluated in the @code{CASE}
+ * macro. @xref{CASE}.
+ *
+ * You do not specify a @code{SELECT} macro with the word ``select''.
+ * Instead, you must use one of the 19 match operators described in
+ * the @code{CASE} macro description.
+=*/
+static macro_t *
+mLoad_Select(templ_t * tpl, macro_t * mac, char const ** pscan)
+{
+ char const * sel_arg;
+ long arg_len = mac->md_res; /* macro len */
+
+ /*
+ * Set the global macro loading mode
+ */
+ load_proc_table = apCaseLoad;
+ mac->md_res = 0;
+ if (arg_len == 0)
+ AG_ABEND_IN(tpl, mac, LD_SEL_EMPTY);
+
+ if (selection_type_complete(tpl, mac, &sel_arg))
+ goto selection_done;
+
+ arg_len -= (intptr_t)sel_arg - mac->md_txt_off;
+ if (arg_len <= 0)
+ AG_ABEND_IN(tpl, mac, LD_SEL_INVAL);
+
+ /*
+ * See if we are doing case insensitive regular expressions
+ * Turn off the case comparison mode for regular expressions.
+ * We don't have to worry about it. It is done for us.
+ */
+ if ( ((int)mac->md_code & (int)FTYP_SELECT_EQV_MATCH_FULL)
+ == (int)FTYP_SELECT_EQV_MATCH_FULL) {
+
+ static unsigned int const bits =
+ ~( unsigned int)FTYP_SELECT_EQUIVALENT_FULL
+ | (unsigned int)FTYP_SELECT_COMPARE_FULL;
+
+ mac->md_res = REG_EXTENDED | REG_ICASE;
+ mac->md_code = (mac_func_t)((unsigned int)mac->md_code & bits);
+ }
+
+ /*
+ * Copy the selection expression, double NUL terminate.
+ */
+ {
+ char * dest = tpl->td_scan;
+ char const * svdest = dest;
+ mac->md_txt_off = (dest - tpl->td_text);
+ if (mac->md_code == FTYP_SELECT_EQUIVALENT) {
+ do {
+ *(dest++) = toupper((uint8_t)*(sel_arg++));
+ } while (--arg_len > 0);
+ } else {
+ memcpy(dest, sel_arg, arg_len);
+ dest += arg_len;
+ }
+ *(dest++) = NUL;
+ *(dest++) = NUL;
+ tpl->td_scan = dest;
+
+ /*
+ * If the value is a quoted string, strip the quotes and
+ * process the string (backslash fixup).
+ */
+ if ((*svdest == '"') || (*svdest == '\''))
+ span_quote((void *)svdest);
+ }
+
+ selection_done:
+ /*
+ * Link this selection macro to the list of selectors for CASE.
+ */
+ current_case.pSelect->md_sib_idx = (mac - tpl->td_macros);
+ current_case.pSelect = (macro_t*)mac;
+
+ return mac + 1;
+}
+
+/**
+ * Free data for a selection macro. Regular expression selections
+ * allocate the compiled re.
+ */
+void
+mUnload_Select(macro_t * mac)
+{
+ if (mac->md_pvt != NULL) {
+ regex_t * regexp = (regex_t*)mac->md_pvt;
+ regfree(regexp);
+ AGFREE(regexp);
+ }
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/funcCase.c */
diff --git a/agen5/funcDef.c b/agen5/funcDef.c
new file mode 100644
index 0000000..80906bd
--- /dev/null
+++ b/agen5/funcDef.c
@@ -0,0 +1,899 @@
+
+/**
+ * @file funcDef.c
+ *
+ * This module implements the DEFINE text function.
+ *
+ * Time-stamp: "2012-04-07 09:47:37 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef int (tCmpProc)(const void*, const void*);
+
+typedef struct def_list def_list_t;
+struct def_list {
+ def_ent_t de;
+ char* pzExpr;
+};
+
+/* = = = START-STATIC-FORWARD = = = */
+static int
+order_def_list(const void* p1, const void* p2);
+
+static def_list_t *
+link_twins(def_list_t * pDL, def_list_t * pNext, int* pCt);
+
+static uint_t
+count_named_values(templ_t * pT, macro_t * mac);
+
+static char *
+gather_assigned_value(char * pzScan, def_list_t * pDL);
+
+static void
+fill_in_values(def_list_t * pDL, char * pzScan, templ_t * pT, macro_t * mac);
+
+static void
+prep_invoke_args(macro_t * mac);
+
+static void
+build_defs(int def_ct, def_list_t * def_list);
+
+static templ_t *
+new_template(templ_t * base_tpl, macro_t * mac, char const * scan);
+
+static void
+load_define_tpl(templ_t * tpl, char const ** ppzScan);
+/* = = = END-STATIC-FORWARD = = = */
+
+static int
+order_def_list(const void* p1, const void* p2)
+{
+ def_ent_t * pDL1 = (def_ent_t*)p1;
+ def_ent_t * pDL2 = (def_ent_t*)p2;
+ int cmp = streqvcmp(pDL1->de_name, pDL2->de_name);
+
+ /*
+ * IF the names are the same, then we order them based on which name
+ * appears first. Do not reorder entries with the same name.
+ */
+ if (cmp == 0)
+ cmp = (int)(pDL1->de_name - pDL2->de_name);
+ return cmp;
+}
+
+static def_list_t *
+link_twins(def_list_t * pDL, def_list_t * pNext, int* pCt)
+{
+ def_list_t * pN;
+ int ct = *pCt;
+ int idx = 1;
+
+ pDL->de.de_twin = &(pNext->de);
+ pNext->de.de_ptwin = &(pDL->de);
+
+ for (;;) {
+ pNext->de.de_index = idx++;
+ pN = pNext + 1; /* We return this, valid or not */
+ if (--ct <= 0) /* count each successive twin */
+ break;
+ if (streqvcmp(pNext->de.de_name, pN->de.de_name) != 0)
+ break;
+
+ /*
+ * We have found another twin. Link it in and advance
+ */
+ pNext->de.de_twin = &(pN->de);
+ pN->de.de_ptwin = &(pNext->de);
+ pNext = pN;
+ }
+
+ pDL->de.de_etwin = &(pNext->de);
+ pNext->de.de_twin = NULL; /* NULL terminated list */
+ pDL->de.de_ptwin = NULL; /* NULL terminated list */
+ *pCt = ct;
+ pDL->de.de_next = NULL; /* in case ct == 0 */
+ return pN; /* If ct is zero, then this is invalid */
+}
+
+
+static uint_t
+count_named_values(templ_t * pT, macro_t * mac)
+{
+ char * pzScan = pT->td_text + mac->md_txt_off;
+ uint_t ct = 0;
+
+ while (*pzScan != NUL) {
+ ct++;
+ if (! IS_VAR_FIRST_CHAR(*pzScan)) {
+ fprintf(stderr, NAMED_VALUES_WHERE, ct, pzScan);
+ AG_ABEND_IN(pT, mac, NAMED_VALUES_NO_NAME);
+ }
+
+ pzScan = SPN_VALUE_NAME_CHARS(pzScan);
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+ if (*pzScan != '=')
+ continue;
+ pzScan = SPN_WHITESPACE_CHARS(pzScan+1);
+ pzScan = (char*)skip_expr(pzScan, strlen(pzScan));
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+ }
+
+ return ct;
+}
+
+
+static char *
+gather_assigned_value(char * pzScan, def_list_t * pDL)
+{
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+ strtransform(pDL->de.de_name, pDL->de.de_name);
+ pDL->pzExpr = pzScan;
+ pDL->de.de_type = VALTYP_TEXT;
+ pzScan = (char*)skip_expr(pzScan, strlen(pzScan));
+
+ /*
+ * Figure out what kind of expression we have
+ */
+ switch (*pDL->pzExpr) {
+ case ';':
+ case '(':
+ /*
+ * These expressions will need evaluation
+ */
+ break;
+
+ case '`':
+ {
+ char* pz;
+ /*
+ * Process the quoted string, but leave a '`' marker, too
+ */
+ AGDUPSTR(pz, pDL->pzExpr, "macro arg expr");
+ span_quote(pz);
+ strcpy(pDL->pzExpr+1, pz);
+ AGFREE((void*)pz);
+ break;
+ }
+ case '"':
+ case '\'':
+ /*
+ * Process the quoted strings now
+ */
+ if ((pzScan - pDL->pzExpr) < 24) {
+ char* pz = (char*)AGALOC(24, "quoted string");
+ memcpy((void*)pz, pDL->pzExpr, (size_t)(pzScan - pDL->pzExpr));
+ pDL->pzExpr = pz;
+ }
+ span_quote(pDL->pzExpr);
+ /* FALLTHROUGH */
+
+ default:
+ /*
+ * Default: the raw sequence of characters is the value
+ */
+ pDL->de.de_val.dvu_text = pDL->pzExpr;
+ pDL->pzExpr = NULL;
+ }
+
+ return pzScan;
+}
+
+
+static void
+fill_in_values(def_list_t * pDL, char * pzScan, templ_t * pT, macro_t * mac)
+{
+ for (;; pDL++ ) {
+ pDL->de.de_name = pzScan;
+ pzScan = SPN_VALUE_NAME_CHARS(pzScan);
+
+ switch (*pzScan) {
+ case NUL:
+ pDL->de.de_val.dvu_text = (char*)zNil;
+ return;
+
+ default:
+ AG_ABEND_IN(pT, mac, FILL_IN_VAL_NO_ASSIGN);
+
+ case ' ': case TAB: case NL: case '\f':
+ *(pzScan++) = NUL;
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+
+ /*
+ * The name was separated by space, but has no value
+ */
+ if (*pzScan != '=') {
+ pDL->de.de_val.dvu_text = (char*)zNil;
+ if (*pzScan == NUL)
+ return;
+ continue;
+ }
+ /* FALLTHROUGH */
+
+ case '=':
+ *(pzScan++) = NUL;
+ }
+
+ /*
+ * When we arrive here, we have just clobbered a '=' char.
+ * Now we have gather up the assigned value.
+ */
+ pzScan = gather_assigned_value(pzScan, pDL);
+
+ /*
+ * IF the next char is NUL, we are done.
+ * OTHERWISE, the next character must be a space
+ */
+ if (*pzScan == NUL)
+ break;
+
+ if (! IS_WHITESPACE_CHAR(*pzScan))
+ AG_ABEND_IN(pT, mac, FILL_IN_VAL_NO_SEP);
+
+ /*
+ * Terminate the string value and skip over any additional space
+ */
+ *(pzScan++) = NUL;
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+ }
+}
+
+/*
+ * parse_mac_args
+ *
+ * This routine is called just before the first call to mFunc_Define
+ * for a particular macro invocation. It scans the text of the invocation
+ * for name-value assignments that are only to live for the duration
+ * of the processing of the user defined macro.
+ */
+LOCAL void
+parse_mac_args(templ_t * pT, macro_t * mac)
+{
+ char * pzScan = pT->td_text + mac->md_txt_off;
+ uint_t ct;
+ def_list_t * pDL;
+ def_list_t * pN;
+
+ /*
+ * If there is no argument text, then the arg count is zero.
+ */
+ if (mac->md_txt_off == 0) {
+ mac->md_res = 0;
+ return;
+ }
+
+ ct = count_named_values(pT, mac);
+
+ /*
+ * The result is zero if we don't have any
+ */
+ mac->md_sib_idx = ct;
+ if (ct == 0) {
+ mac->md_txt_off = 0;
+ mac->md_res = 0;
+ return;
+ }
+
+ /*
+ * Allocate the array of definition descriptors
+ */
+ pzScan = pT->td_text + mac->md_txt_off;
+ pDL = (def_list_t *)AGALOC(ct * sizeof(def_list_t), "array of def desc");
+ memset((void*)pDL, 0, ct * sizeof(def_list_t));
+ mac->md_res = (uintptr_t)pDL;
+
+ /*
+ * Fill in the array of value assignments
+ */
+ fill_in_values(pDL, pzScan, pT, mac);
+
+ if (ct > 1) {
+ /*
+ * There was more than one value assignment.
+ * Sort them just so we know the siblings are together.
+ * Order is preserved by comparing string addresses,
+ * if the strings compare equal.
+ */
+ pDL = (def_list_t *)mac->md_res;
+ qsort((void*)pDL, (size_t)ct, sizeof(def_list_t), order_def_list);
+
+ /*
+ * Now, link them all together. Singly linked list.
+ */
+ for (;;) {
+ if (--ct == 0) {
+ pDL->de.de_next = NULL;
+ break;
+ }
+
+ pN = pDL + 1;
+
+ /*
+ * IF the next entry has the same name,
+ * THEN it is a "twin". Link twins on the twin list.
+ */
+ if (streqvcmp(pDL->de.de_name, pN->de.de_name) == 0) {
+ pN = link_twins(pDL, pN, (int*)&ct);
+ if (ct <= 0)
+ break; /* pN is now invalid */
+ }
+
+ pDL->de.de_next = &(pN->de);
+ pDL = pN;
+ }
+ }
+}
+
+/**
+ * prepare the args for INVOKE macro.
+ * See if there's any text following the name of the DEFINE macro to invoke.
+ * If there is, then set up the arguments now so it's easy to deal with
+ * next time around. The name of the macro to invoke may be dynamic.
+ * "skip_expr" skips over either a name or a scheme expression that
+ * is supposed to yield a name.
+ *
+ * @param mac the macro structure describing the invocation
+ */
+static void
+prep_invoke_args(macro_t * mac)
+{
+ char * pzText;
+ templ_t * pT = current_tpl;
+
+ if (mac->md_txt_off == 0)
+ AG_ABEND_IN(pT, mac, PREP_INVOKE_NO_NAME);
+ mac->md_name_off = mac->md_txt_off;
+ pzText = pT->td_text + mac->md_txt_off;
+ pzText = (char*)skip_expr(pzText, strlen(pzText));
+
+ /*
+ * IF there is no more text,
+ * THEN there are no arguments
+ */
+ if (*pzText == NUL) {
+ mac->md_txt_off = 0;
+ mac->md_res = 0;
+ }
+
+ /*
+ * OTHERWISE, skip to the start of the text and process
+ * the arguments to the macro
+ */
+ else {
+ if (! IS_WHITESPACE_CHAR(*pzText))
+ AG_ABEND_IN(pT, mac, PREP_INVOKE_NO_SEP);
+ *pzText = NUL;
+ pzText = SPN_WHITESPACE_CHARS(pzText + 1);
+ mac->md_txt_off = pzText - pT->td_text;
+ parse_mac_args(pT, mac);
+ current_tpl = pT;
+ }
+}
+
+/*=macfunc DEBUG
+ *
+ * handler-proc:
+ * load-proc:
+ *
+ * what: Print debug message to trace output
+ * desc:
+ *
+ * If the tracing level is at "debug-message" or above
+ * (@pxref{autogen trace}), this macro prints a debug message to trace
+ * output. This message is not evaluated. This macro can also be used to
+ * set useful debugger breakpoints. By inserting [+DEBUG n+] into your
+ * template, you can set a debugger breakpoint on the #n case element
+ * below (in the AutoGen source) and step through the processing of
+ * interesting parts of your template.
+ *
+ * To be useful, you have to have access to the source tree where autogen
+ * was built and the template being processed. The definitions are also
+ * helpful, but not crucial. Please contact the author if you think you
+ * might actually want to use this.
+=*/
+macro_t *
+mFunc_Debug(templ_t * pT, macro_t * mac)
+{
+ static int dummy = 0;
+ char const * pz = pT->td_text + mac->md_txt_off;
+ int for_index = (curr_ivk_info->ii_for_depth <= 0)
+ ? -1
+ : curr_ivk_info->ii_for_data[ curr_ivk_info->ii_for_depth-1].for_index;
+
+ fprintf(trace_fp, FN_DEBUG, pz, for_index);
+
+ /*
+ * The case element values were chosen to thwart most
+ * optimizers that might be too bright for its own good.
+ * (`dummy' is write-only and could be ignored)
+ */
+ do {
+ if (IS_DEC_DIGIT_CHAR(*pz)) {
+ for_index = atoi(pz);
+ break;
+ }
+ } while (*(pz++) != NUL);
+
+ if (for_index < 0)
+ for_index = -1;
+
+ switch (for_index) {
+ case -1: dummy = 'X'; break;
+ case 0: dummy = 'A'; break;
+ case 1: dummy = 'u'; break;
+ case 2: dummy = 't'; break;
+ case 3: dummy = 'o'; break;
+ case 4: dummy = 'G'; break;
+ case 5: dummy = 'e'; break;
+ case 6: dummy = 'n'; break;
+ case 7: dummy = 'N'; break;
+ case 8: dummy = 'U'; break;
+ case 9: dummy = 'T'; break;
+ case 10: dummy = '.'; break;
+ default: dummy++;
+ }
+ if (IS_GRAPHIC_CHAR(dummy))
+ fprintf(trace_fp, FN_DEBUG_GRAPHIC, dummy);
+ putc(NL, trace_fp);
+ return mac+1;
+}
+
+/**
+ * Build up a definition context.
+ * It is created by passed-in macro arguments.
+ *
+ * @param def_ct number of definitions
+ * @param def_list list of definitions
+ */
+static void
+build_defs(int def_ct, def_list_t * def_list)
+{
+ curr_def_ctx.dcx_defent = &(def_list->de);
+
+ /*
+ * FOR each definition, evaluate the associated expression
+ * and set the text value to it.
+ */
+ do {
+ if (def_list->pzExpr == NULL)
+ continue;
+
+ retryExpression:
+ switch (*(def_list->pzExpr)) {
+ case ';':
+ {
+ char* pz = strchr(def_list->pzExpr, NL);
+ if (pz != NULL) {
+ pz = SPN_WHITESPACE_CHARS(pz + 1);
+ def_list->pzExpr = pz;
+ goto retryExpression;
+ }
+ /* FALLTHROUGH */
+ }
+ case NUL:
+ def_list->pzExpr = NULL;
+ def_list->de.de_val.dvu_text = (char*)zNil;
+ break;
+
+ case '(':
+ {
+ SCM res;
+
+ /*
+ * It is a scheme expression. Accept only string
+ * and number results.
+ */
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) {
+ fprintf(trace_fp, TRACE_BUILD_DEFS,
+ cur_macro->md_sib_idx - def_ct, def_list->pzExpr);
+ }
+
+ res = ag_eval(def_list->pzExpr);
+ if (AG_SCM_STRING_P(res)) {
+ AGDUPSTR(def_list->de.de_val.dvu_text,
+ ag_scm2zchars(res, "res"), "ev res");
+ }
+ else if (AG_SCM_NUM_P(res)) {
+ def_list->de.de_val.dvu_text = AGALOC(16, "num buf");
+ snprintf(def_list->de.de_val.dvu_text, (size_t)16,
+ BUILD_DEFS_LONG_FMT, AG_SCM_TO_ULONG(res));
+ }
+ else
+ AGDUPSTR(def_list->de.de_val.dvu_text, zNil, "nil str");
+ break;
+ }
+
+ case '`':
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) {
+ fprintf(trace_fp, TRACE_BUILD_DEFS,
+ cur_macro->md_sib_idx - def_ct, def_list->pzExpr+1);
+ }
+ def_list->de.de_val.dvu_text = shell_cmd(def_list->pzExpr+1);
+ break;
+ }
+ } while (def_list++, --def_ct > 0);
+}
+
+/*=macfunc DEFINE
+ *
+ * what: Define a user AutoGen macro
+ * cindex: define macro
+ * handler_proc:
+ * load_proc:
+ * unload-proc:
+ *
+ * desc:
+ *
+ * This function will define a new macro. You must provide a name for the
+ * macro. You do not specify any arguments, though the invocation may
+ * specify a set of name/value pairs that are to be active during the
+ * processing of the macro.
+ *
+ * @example
+ * [+ define foo +]
+ * ... macro body with macro functions ...
+ * [+ enddef +]
+ * ... [+ foo bar='raw text' baz=<<text expression>> +]
+ * @end example
+ *
+ * Once the macro has been defined, this new macro can be invoked by
+ * specifying the macro name as the first token after the start macro marker.
+ * Alternatively, you may make the invocation explicitly invoke a defined
+ * macro by specifying @code{INVOKE} (@pxref{INVOKE}) in the macro
+ * invocation. If you do that, the macro name can be computed with an
+ * expression that gets evaluated every time the INVOKE macro is encountered.
+ *
+ * Any remaining text in the macro invocation will be used to create new
+ * name/value pairs that only persist for the duration of the processing of
+ * the macro. The expressions are evaluated the same way basic
+ * expressions are evaluated. @xref{expression syntax}.
+ *
+ * The resulting definitions are handled much like regular
+ * definitions, except:
+ *
+ * @enumerate
+ * @item
+ * The values may not be compound. That is, they may not contain
+ * nested name/value pairs.
+ * @item
+ * The bindings go away when the macro is complete.
+ * @item
+ * The name/value pairs are separated by whitespace instead of
+ * semi-colons.
+ * @item
+ * Sequences of strings are not concatenated.
+ * @end enumerate
+ *
+ * @quotation
+ * @strong{NB:} The macro is extracted from the template as the template is
+ * scanned. You cannot conditionally define a macro by enclosing it in an
+ * @code{IF}/@code{ENDIF} (@pxref{IF}) macro pair. If you need to dynamically
+ * select the format of a @code{DEFINE}d macro, then put the flavors into
+ * separate template files that simply define macros. @code{INCLUDE}
+ * (@pxref{INCLUDE}) the appropriate template when you have computed which
+ * you need.
+ * @end quotation
+ *
+ * Due to this, it is acceptable and even a good idea to place all the
+ * @code{DEFINE} macros at the end of the template. That puts the main
+ * body of the template at the beginning of the file.
+=*/
+/*=macfunc ENDDEF
+ *
+ * what: Ends a macro definition.
+ * in-context:
+ *
+ * desc:
+ * This macro ends the @code{DEFINE} function template block.
+ * For a complete description @xref{DEFINE}.
+=*/
+/**
+ * This routine runs the invocation of a defined macro.
+ *
+ * @param tpl not used
+ * @param[in] mac the macro that holds the data for the defined macro
+ */
+macro_t *
+mFunc_Define(templ_t * tpl, macro_t * mac)
+{
+ def_list_t * pList = (def_list_t *)mac->md_res;
+ int defCt = mac->md_sib_idx;
+ def_ctx_t ctx;
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING) {
+ tpl = (templ_t*)mac->md_pvt;
+
+ fprintf(trace_fp, TPL_INVOKED, tpl->td_name, defCt);
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT,
+ current_tpl->td_file, mac->md_line);
+ }
+
+ /*
+ * IF we have no special definitions, then do not nest definitions
+ */
+ if (defCt != 0) {
+ ctx = curr_def_ctx;
+ curr_def_ctx.dcx_prev = &ctx;
+ build_defs(defCt, pList);
+ }
+
+ gen_new_block((templ_t*)mac->md_pvt);
+
+ if (defCt != 0)
+ curr_def_ctx = ctx;
+
+ if ((defCt = mac->md_sib_idx) > 0) {
+ pList = (def_list_t *)mac->md_res;
+ while (defCt-- > 0) {
+ if (pList->pzExpr != NULL) {
+ AGFREE((void*)pList->de.de_val.dvu_text);
+ pList->de.de_val.dvu_text = NULL;
+ }
+ pList++;
+ }
+ }
+
+ return mac+1;
+}
+
+/**
+ * unload a defined macro
+ *
+ * @param[in,out] mac macro containing the data to unload
+ */
+void
+mUnload_Define(macro_t * mac)
+{
+ void * p = (void*)(mac->md_res);
+ if (p != NULL)
+ AGFREE(p);
+}
+
+/*=macfunc INVOKE
+ *
+ * handler_proc:
+ * what: Invoke a User Defined Macro
+ *
+ * desc:
+ *
+ * User defined macros may be invoked explicitly or implicitly.
+ * If you invoke one implicitly, the macro must begin with the
+ * name of the defined macro. Consequently, this may @strong{not}
+ * be a computed value. If you explicitly invoke a user defined macro,
+ * the macro begins with the macro name @code{INVOKE} followed by
+ * a @i{basic expression} that must yield a known user defined macro.
+ * A macro name _must_ be found, or AutoGen will issue a diagnostic
+ * and exit.
+ *
+ * Arguments are passed to the invoked macro by name.
+ * The text following the macro name must consist of a series of
+ * names each of which is followed by an equal sign (@code{=}) and
+ * a @i{basic expression} that yields a string.
+ *
+ * The string values may contain template macros that are parsed
+ * the first time the macro is processed and evaluated again every
+ * time the macro is evaluated.
+=*/
+macro_t *
+mFunc_Invoke(templ_t * tpl, macro_t * mac)
+{
+ /*
+ * IF this is the first time through,
+ * THEN separate the name from the rest of the arguments.
+ */
+ if (mac->md_name_off == 0) {
+ prep_invoke_args(mac);
+
+ /*
+ * IF the name is constant and not an expression,
+ * THEN go find the template now and bind the macro call
+ * to a particular template macro
+ */
+ if (IS_VAR_FIRST_CHAR(tpl->td_text[ mac->md_name_off ])) {
+ mac->md_code = FTYP_DEFINE;
+ mac->md_pvt = (void*)find_tpl(tpl->td_text + mac->md_name_off);
+
+ if (mac->md_pvt == NULL) {
+ char const * p = tpl->td_text + mac->md_name_off;
+ AG_ABEND_IN(tpl, mac, aprf(BAD_MAC_NM_FMT, p));
+ /* NOTREACHED */
+ }
+
+ return mFunc_Define(tpl, mac);
+ }
+ }
+
+ /*
+ * Call `eval' to determine the macro name. Every invocation
+ * may be different!!
+ */
+ {
+ SCM macName = eval(tpl->td_text + mac->md_name_off);
+ char * pz = ag_scm2zchars(macName, "mac name");
+ templ_t * ntpl = find_tpl(pz);
+ if (ntpl == NULL) {
+ pz = aprf(BAD_MAC_NM_FMT, pz);
+ AG_ABEND_IN(tpl, mac, pz);
+ /* NOTREACHED */
+ }
+
+ mac->md_pvt = (void*)ntpl;
+ }
+ return mFunc_Define(tpl, mac);
+}
+
+/**
+ * Loads the debug function for load time breakpoints.
+ * @param pT containing template
+ * @param pMac the debug macro data
+ * @param ppzSan pointer to scanning pointer
+ * @returns the next open macro slot
+ */
+macro_t *
+mLoad_Debug(templ_t * pT, macro_t * pMac, char const ** ppzScan)
+{
+ if (OPT_VALUE_TRACE >= TRACE_DEBUG_MESSAGE)
+ return mLoad_Unknown(pT, pMac, ppzScan);
+ return mLoad_Comment(pT, pMac, ppzScan);
+}
+
+static templ_t *
+new_template(templ_t * base_tpl, macro_t * mac, char const * scan)
+{
+ templ_t * ntpl;
+ char * copy;
+ char const * src = (char const *)mac->md_txt_off;
+ int ct = base_tpl->td_mac_ct - (mac - base_tpl->td_macros);
+ size_t aloc_sz =
+ sizeof(*ntpl) + (ct * sizeof(macro_t)) + strlen(scan) + 0x100;
+ aloc_sz &= ~0x0F;
+
+ /*
+ * Allocate a new template block that is much larger than needed.
+ */
+ ntpl = (templ_t*)AGALOC(aloc_sz, "new tpl");
+ memset((void*)ntpl, 0, aloc_sz);
+ ntpl->td_magic = base_tpl->td_magic;
+ ntpl->td_size = aloc_sz;
+ ntpl->td_mac_ct = ct;
+ ntpl->td_file = strdup(base_tpl->td_file);
+
+ copy = ntpl->td_name = (void*)(ntpl->td_macros + ct);
+ if (! IS_VAR_FIRST_CHAR(*src))
+ AG_ABEND_IN(base_tpl, mac, LD_DEF_NEED_NAME);
+
+ while (IS_VALUE_NAME_CHAR(*src))
+ *(copy++) = *(src++);
+
+ *(copy++) = NUL;
+
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS)
+ fprintf(trace_fp, TRACE_MACRO_DEF, ntpl->td_name, ntpl->td_file);
+
+ ntpl->td_text = copy;
+ ntpl->td_scan = copy+1;
+ strcpy(ntpl->td_start_mac, base_tpl->td_start_mac);
+ strcpy(ntpl->td_end_mac, base_tpl->td_end_mac);
+ current_tpl = ntpl;
+
+ return ntpl;
+}
+
+static void
+load_define_tpl(templ_t * tpl, char const ** ppzScan)
+{
+ macro_t * last_mac = parse_tpl(tpl->td_macros, ppzScan);
+ int ct;
+
+ /*
+ * Make sure all of the input string was *NOT* scanned.
+ */
+ if (*ppzScan == NULL)
+ AG_ABEND_IN(tpl, tpl->td_macros, LD_DEF_WOOPS);
+
+ ct = last_mac - tpl->td_macros;
+
+ /*
+ * IF there are empty macro slots,
+ * THEN pack the text
+ */
+ if (ct < tpl->td_mac_ct) {
+ int delta = sizeof(macro_t) * (tpl->td_mac_ct - ct);
+ void * data = (tpl->td_name == NULL) ? tpl->td_text : tpl->td_name;
+ size_t size = tpl->td_scan - (char*)data;
+ memmove((void*)last_mac, data, size);
+
+ tpl->td_text -= delta;
+ tpl->td_scan -= delta;
+ tpl->td_name -= delta;
+ tpl->td_mac_ct = ct;
+ }
+}
+
+macro_t *
+mLoad_Define(templ_t * ori_tpl, macro_t * mac, char const ** p_scan)
+{
+ static load_proc_p_t apDefineLoad[ FUNC_CT ] = { NULL };
+
+ templ_t * new_tpl;
+
+ /**
+ * Save the global macro loading mode
+ */
+ load_proc_p_t const * save_load_procs = load_proc_table;
+
+ if (mac->md_txt_off == 0)
+ AG_ABEND_IN(ori_tpl, mac, LD_DEF_NEED_NAME);
+
+ /*
+ * IF this is the first time here, THEN set up the "DEFINE" block
+ * callout table. It is the standard table, except entries are
+ * inserted for functions that are enabled only while processing
+ * a DEFINE block (viz. "ENDDEF" and removing "DEFINE").
+ */
+ if (apDefineLoad[0] == NULL) {
+ memcpy((void*)apDefineLoad, base_load_table, sizeof(base_load_table));
+ apDefineLoad[ FTYP_ENDDEF ] = &mLoad_Ending;
+ apDefineLoad[ FTYP_DEFINE ] = &mLoad_Bogus;
+ }
+
+ load_proc_table = apDefineLoad;
+ defining_macro = true;
+ new_tpl = new_template(ori_tpl, mac, *p_scan);
+ load_define_tpl(new_tpl, p_scan);
+ defining_macro = false;
+
+ /*
+ * Adjust the sizes. Remove absolute pointers. Reallocate to the correct
+ * size. Restore the offsets to pointer values.
+ */
+ {
+ size_t sz = new_tpl->td_scan - (char*)new_tpl;
+ if (sz < new_tpl->td_size) {
+ new_tpl->td_size = sz;
+ new_tpl->td_name -= (long)new_tpl;
+ new_tpl->td_text -= (long)new_tpl;
+ new_tpl = AGREALOC((void*)new_tpl, new_tpl->td_size, "resize mac");
+ new_tpl->td_name += (long)new_tpl;
+ new_tpl->td_text += (long)new_tpl;
+ }
+ }
+
+#if defined(DEBUG_ENABLED)
+ if (HAVE_OPT(SHOW_DEFS)) {
+ static char const zSum[] = "loaded %d macros from %s\n"
+ "\tBinary template size: 0x%X\n\n";
+ fprintf(trace_fp, zSum, new_tpl->td_mac_ct, new_tpl->td_file,
+ (unsigned int)new_tpl->td_size);
+ }
+#endif
+
+ new_tpl->td_scan = (char*)named_tpls;
+ named_tpls = new_tpl;
+ load_proc_table = save_load_procs;
+ memset((void*)mac, 0, sizeof(*mac));
+ current_tpl = ori_tpl;
+ return mac;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/funcDef.c */
diff --git a/agen5/funcEval.c b/agen5/funcEval.c
new file mode 100644
index 0000000..5775a34
--- /dev/null
+++ b/agen5/funcEval.c
@@ -0,0 +1,651 @@
+
+/**
+ * @file funcEval.c
+ *
+ * This module evaluates macro expressions.
+ *
+ * Time-stamp: "2012-04-07 09:41:46 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static inline char const *
+tpl_text(templ_t * tpl, macro_t * mac);
+
+static void
+tpl_warning(templ_t * tpl, macro_t * mac, char const * msg);
+
+static int
+expr_type(char * pz);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * Convert SCM to displayable string.
+ * @param s the input scm
+ * @returns a string a human can read
+ */
+LOCAL char const *
+scm2display(SCM s)
+{
+ static char z[48];
+ char const * pzRes = z;
+
+ switch (ag_scm_type_e(s)) {
+ case GH_TYPE_BOOLEAN:
+ z[0] = AG_SCM_NFALSEP(s) ? '1' : '0'; z[1] = NUL;
+ break;
+
+ case GH_TYPE_STRING:
+ case GH_TYPE_SYMBOL:
+ pzRes = ag_scm2zchars(s, "SCM Result");
+ break;
+
+ case GH_TYPE_CHAR:
+ z[0] = AG_SCM_CHAR(s); z[1] = NUL; break;
+
+ case GH_TYPE_VECTOR:
+ pzRes = RESOLVE_SCM_VECTOR; break;
+
+ case GH_TYPE_PAIR:
+ pzRes = RESOLVE_SCM_PAIR; break;
+
+ case GH_TYPE_NUMBER:
+ snprintf(z, sizeof(z), RESOLVE_SCM_NUMBER, AG_SCM_TO_ULONG(s)); break;
+
+ case GH_TYPE_PROCEDURE:
+#ifdef SCM_SUBR_ENTRY
+ {
+ void * x = &SCM_SUBR_ENTRY(s);
+
+ snprintf(z, sizeof(z), RESOLVE_SCM_PROC,
+ (unsigned long)x);
+ break;
+ }
+#else
+ pzRes = "** PROCEDURE **";
+ break;
+#endif
+
+ case GH_TYPE_LIST:
+ pzRes = RESOLVE_SCM_LIST; break;
+
+ case GH_TYPE_INEXACT:
+ pzRes = RESOLVE_SCM_INEXACT; break;
+
+ case GH_TYPE_EXACT:
+ pzRes = RESOLVE_SCM_EXACT; break;
+
+ case GH_TYPE_UNDEFINED:
+ pzRes = (char*)zNil; break;
+
+ default:
+ pzRes = RESOLVE_SCM_UNKNOWN; break;
+ }
+
+ return pzRes;
+}
+
+/**
+ * Return the text associated with a macro.
+ */
+static inline char const *
+tpl_text(templ_t * tpl, macro_t * mac)
+{
+ if (mac->md_txt_off == 0)
+ return zNil;
+
+ return tpl->td_text + mac->md_txt_off;
+}
+
+static void
+tpl_warning(templ_t * tpl, macro_t * mac, char const * msg)
+{
+ fprintf(trace_fp, TPL_WARN_FMT, tpl->td_file, mac->md_line, msg);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Evaluate an expression and return a string pointer. Always.
+ * It may need to be deallocated, so a boolean pointer is used
+ * to tell the caller. Also, the match existence and non-existence
+ * pay attention to the address of the empty string that gets
+ * returned. If it is, specifically, "zNil", then this code is
+ * saying, "we could not compute a value, so we're returning this
+ * empty string". This is used by the Select_Match_Existence and
+ * Select_Match_NonExistence code to distinguish non-existence from
+ * an empty string value.
+ *
+ * @param allocated tell caller if result has been allocated
+ * @returns a string representing the result.
+ */
+LOCAL char const *
+eval_mac_expr(bool * allocated)
+{
+ templ_t * tpl = current_tpl;
+ macro_t * mac = cur_macro;
+ int code = mac->md_res;
+ char const * text = NULL; /* warning patrol */
+ def_ent_t * ent;
+
+ *allocated = false;
+
+ if ((code & EMIT_NO_DEFINE) != 0) {
+ text = tpl_text(tpl, mac);
+ code &= EMIT_PRIMARY_TYPE;
+ ent = NULL; /* warning patrol */
+ }
+
+ else {
+ /*
+ * Get the named definition entry, maybe
+ */
+ bool indexed;
+ ent = find_def_ent(tpl->td_text + mac->md_name_off, &indexed);
+
+ if (ent == NULL) {
+ switch (code & (EMIT_IF_ABSENT | EMIT_ALWAYS)) {
+ case EMIT_IF_ABSENT:
+ /*
+ * There is only one expression. It applies because
+ * we did not find a definition.
+ */
+ text = tpl_text(tpl, mac);
+ code &= EMIT_PRIMARY_TYPE;
+ break;
+
+ case EMIT_ALWAYS:
+ /*
+ * There are two expressions. Take the second one.
+ */
+ text = tpl->td_text + mac->md_end_idx;
+ code = ((code & EMIT_SECONDARY_TYPE)
+ >> EMIT_SECONDARY_SHIFT);
+ break;
+
+ case 0:
+ /*
+ * Emit only if found
+ */
+ return no_def_str;
+
+ case (EMIT_IF_ABSENT | EMIT_ALWAYS):
+ /*
+ * Emit inconsistently :-}
+ */
+ AG_ABEND_IN(tpl, mac, EVAL_EXPR_PROG_ERR);
+ /* NOTREACHED */
+ }
+ }
+
+ /*
+ * OTHERWISE, we found an entry. Make sure we were supposed to.
+ */
+ else {
+ if ((code & EMIT_IF_ABSENT) != 0)
+ return (char*)zNil;
+
+ if ( (ent->de_type != VALTYP_TEXT)
+ && ((code & EMIT_PRIMARY_TYPE) == EMIT_VALUE) ) {
+ tpl_warning(tpl, mac, EVAL_EXPR_BLOCK_IN_EVAL);
+ return (char*)zNil;
+ }
+
+ /*
+ * Compute the expression string. There are three possibilities:
+ * 1. There is an expression string in the macro, but it must
+ * be formatted with the text value.
+ * Make sure we have a value.
+ * 2. There is an expression string in the macro, but it is *NOT*
+ * to be formatted. Use it as is. Do *NOT* verify that
+ * the define value is text.
+ * 3. There is no expression with the macro invocation.
+ * The define value *must* be text.
+ */
+ if ((code & EMIT_FORMATTED) != 0) {
+ /*
+ * And make sure what we found is a text value
+ */
+ if (ent->de_type != VALTYP_TEXT) {
+ tpl_warning(tpl, mac, EVAL_EXPR_BLOCK_IN_EVAL);
+ return (char*)zNil;
+ }
+
+ *allocated = true;
+ text = aprf(tpl_text(tpl, mac), ent->de_val.dvu_text);
+ }
+
+ else if (mac->md_txt_off != 0)
+ text = tpl->td_text + mac->md_txt_off;
+
+ else {
+ /*
+ * And make sure what we found is a text value
+ */
+ if (ent->de_type != VALTYP_TEXT) {
+ tpl_warning(tpl, mac, EVAL_EXPR_BLOCK_IN_EVAL);
+ return (char*)zNil;
+ }
+
+ text = ent->de_val.dvu_text;
+ }
+
+ code &= EMIT_PRIMARY_TYPE;
+ }
+ }
+
+ /*
+ * The "code" tells us how to handle the expression
+ */
+ switch (code) {
+ case EMIT_VALUE:
+ assert(ent != NULL);
+ if (*allocated) {
+ AGFREE((void*)text);
+ *allocated = false;
+ }
+
+ text = ent->de_val.dvu_text;
+ break;
+
+ case EMIT_EXPRESSION:
+ {
+ SCM res = ag_eval(text);
+
+ if (*allocated) {
+ AGFREE((void*)text);
+ *allocated = false;
+ }
+
+ text = scm2display(res);
+ break;
+ }
+
+ case EMIT_SHELL:
+ {
+ char* pz = shell_cmd(text);
+
+ if (*allocated)
+ AGFREE((void*)text);
+
+ if (pz != NULL) {
+ *allocated = true;
+ text = pz;
+ }
+ else {
+ *allocated = false;
+ text = (char*)zNil;
+ }
+ break;
+ }
+
+ case EMIT_STRING:
+ break;
+ }
+
+ return text;
+}
+
+/*=gfunc error_source_line
+ *
+ * what: display of file & line
+ * general_use:
+ * doc: This function is only invoked just before Guile displays
+ * an error message. It displays the file name and line number
+ * that triggered the evaluation error. You should not need to
+ * invoke this routine directly. Guile will do it automatically.
+=*/
+SCM
+ag_scm_error_source_line(void)
+{
+ fprintf(stderr, SCM_ERROR_FMT, current_tpl->td_name, cur_macro->md_line,
+ current_tpl->td_text + cur_macro->md_txt_off);
+ fflush(stderr);
+
+ return SCM_UNDEFINED;
+}
+
+
+/*=gfunc emit
+ *
+ * what: emit the text for each argument
+ *
+ * exparg: alist, list of arguments to stringify and emit, , list
+ *
+ * doc: Walk the tree of arguments, displaying the values of displayable
+ * SCM types. EXCEPTION: if the first argument is a number, then
+ * that number is used to index the output stack. "0" is the default,
+ * the current output.
+=*/
+SCM
+ag_scm_emit(SCM val)
+{
+ static int depth = 0;
+ static FILE * fp;
+
+ switch (depth) {
+ case 1:
+ {
+ out_stack_t* pSaveFp;
+ unsigned long pnum;
+
+ if (! AG_SCM_NUM_P(val))
+ break;
+
+ pSaveFp = cur_fpstack;
+ pnum = AG_SCM_TO_ULONG(val);
+
+ for (; pnum > 0; pnum--) {
+ pSaveFp = pSaveFp->stk_prev;
+ if (pSaveFp == NULL)
+ AG_ABEND(aprf(EMIT_INVAL_PORT, AG_SCM_TO_ULONG(val)));
+ }
+
+ fp = pSaveFp->stk_fp;
+ return SCM_UNDEFINED;
+ }
+
+ case 0:
+ fp = cur_fpstack->stk_fp; // initialize the first time through
+ break;
+ }
+
+ depth++;
+ for (;;) {
+ if (val == SCM_UNDEFINED)
+ break;
+
+ if (AG_SCM_NULLP(val))
+ break;
+
+ if (AG_SCM_STRING_P(val)) {
+ fputs((char*)ag_scm2zchars(val, "emit val"), fp);
+ fflush(fp);
+ break;
+ }
+
+ switch (ag_scm_type_e(val)) {
+ case GH_TYPE_LIST:
+ case GH_TYPE_PAIR:
+ ag_scm_emit(SCM_CAR(val));
+ val = SCM_CDR(val);
+ continue;
+
+ default:
+ fputs(scm2display(val), fp);
+ fflush(fp);
+ break;
+ }
+
+ break;
+ }
+
+ depth--;
+ return SCM_UNDEFINED;
+}
+
+/**
+ * The global evaluation function.
+ *
+ * The string to "evaluate" may be a literal string, or may need Scheme
+ * interpretation. So, we do one of three things: if the string starts with
+ * a Scheme comment character or evaluation character (';' or '('), then run
+ * a Scheme eval. If it starts with a quote character ('\'' or '"'), then
+ * digest the string and return that. Otherwise, just return the string.
+ *
+ * @param expr input expression string
+ * @returns an SCM value representing the result
+ */
+LOCAL SCM
+eval(char const * expr)
+{
+ bool allocated = false;
+ char * pzTemp;
+ SCM res;
+
+ switch (*expr) {
+ case '(':
+ case ';':
+ res = ag_eval((char*)expr);
+ break;
+
+ case '`':
+ AGDUPSTR(pzTemp, expr, "shell script");
+ (void)span_quote(pzTemp);
+ expr = shell_cmd(pzTemp);
+ AGFREE((void*)pzTemp);
+ res = AG_SCM_STR02SCM((char*)expr);
+ AGFREE((void*)expr);
+ break;
+
+ case '"':
+ case '\'':
+ AGDUPSTR(pzTemp, expr, "quoted string");
+ (void)span_quote(pzTemp);
+ allocated = true;
+ expr = pzTemp;
+ /* FALLTHROUGH */
+
+ default:
+ res = AG_SCM_STR02SCM((char*)expr);
+ if (allocated)
+ AGFREE((void*)expr);
+ }
+
+ return res;
+}
+
+
+/*=macfunc EXPR
+ *
+ * what: Evaluate and emit an Expression
+ * alias: + - + ? + % + ; + ( + '`' + '"' + "'" + . +
+ *
+ * handler_proc:
+ * load_proc:
+ *
+ * desc:
+ * This macro does not have a name to cause it to be invoked
+ * explicitly, though if a macro starts with one of the apply codes
+ * or one of the simple expression markers, then an expression
+ * macro is inferred. The result of the expression evaluation
+ * (@pxref{expression syntax}) is written to the current output.
+=*/
+macro_t*
+mFunc_Expr(templ_t * tpl, macro_t * mac)
+{
+ bool allocated_str;
+ char const * pz = eval_mac_expr(&allocated_str);
+
+ (void)tpl;
+
+ if (*pz != NUL) {
+ fputs(pz, cur_fpstack->stk_fp);
+ fflush(cur_fpstack->stk_fp);
+ }
+
+ if (allocated_str)
+ AGFREE((void*)pz);
+
+ return mac + 1;
+}
+
+/**
+ * Determine the expression type. It may be Scheme (starts with a semi-colon
+ * or an opening parenthesis), a shell command (starts with a back tick),
+ * a quoted string (either single or double), or it is some sort of plain
+ * string. In that case, just return the text.
+ *
+ * @param pz pointer to string to diagnose
+ * @returns the EMIT_* compound value, though actually only
+ * EXPRESSION, SHELL or STRING can really ever be returned.
+ */
+static int
+expr_type(char * pz)
+{
+ switch (*pz) {
+ case ';':
+ case '(':
+ return EMIT_EXPRESSION;
+
+ case '`':
+ span_quote(pz);
+ return EMIT_SHELL;
+
+ case '"':
+ case '\'':
+ span_quote(pz);
+ /* FALLTHROUGH */
+
+ default:
+ return EMIT_STRING;
+ }
+}
+
+
+/**
+ * mLoad_Expr
+ */
+macro_t *
+mLoad_Expr(templ_t * tpl, macro_t * mac, char const ** ppzScan)
+{
+ char * copy; /* next text dest */
+ char const * src = (char const*)mac->md_txt_off; /* macro text */
+ size_t src_len = (long)mac->md_res; /* macro len */
+ char const * end_src = src + src_len;
+
+ if (src_len == 0) {
+ if (mac->md_code == FTYP_INCLUDE)
+ AG_ABEND_IN(tpl, mac, LD_INC_NO_FNAME);
+ mac->md_res = EMIT_VALUE;
+ mac->md_txt_off = 0;
+ return mac + 1;
+ }
+
+ switch (*src) {
+ case '-':
+ mac->md_res = EMIT_IF_ABSENT;
+ src++;
+ break;
+
+ case '?':
+ mac->md_res = EMIT_ALWAYS;
+ src++;
+ if (*src == '%') {
+ mac->md_res |= EMIT_FORMATTED;
+ src++;
+ }
+ break;
+
+ case '%':
+ mac->md_res = EMIT_FORMATTED;
+ src++;
+ break;
+
+ case '`':
+ (void) mLoad_Unknown(tpl, mac, ppzScan);
+ mac->md_res = EMIT_NO_DEFINE | EMIT_SHELL;
+ span_quote(tpl->td_text + mac->md_txt_off);
+ return mac + 1;
+
+ case '"':
+ case '\'':
+ (void) mLoad_Unknown(tpl, mac, ppzScan);
+ mac->md_res = EMIT_NO_DEFINE | EMIT_STRING;
+ span_quote(tpl->td_text + mac->md_txt_off);
+ return mac + 1;
+
+ case '(':
+ case ';':
+ (void) mLoad_Unknown(tpl, mac, ppzScan);
+ mac->md_res = EMIT_NO_DEFINE | EMIT_EXPRESSION;
+ return mac + 1;
+
+ default:
+ mac->md_res = EMIT_VALUE; /* zero */
+ break;
+ }
+
+ copy = tpl->td_scan;
+ mac->md_name_off = (copy - tpl->td_text);
+ {
+ size_t remLen = canonical_name(copy, src, (int)src_len);
+ if (remLen > src_len)
+ AG_ABEND_IN(tpl, mac, LD_EXPR_BAD_NAME);
+ src += src_len - remLen;
+ src_len = remLen;
+ copy += strlen(copy) + 1;
+ }
+
+ if (src >= end_src) {
+ if (mac->md_res != EMIT_VALUE)
+ AG_ABEND_IN(tpl, mac, LD_EXPR_NO_TEXT);
+
+ mac->md_txt_off = 0;
+
+ } else {
+ char* pz = copy;
+ src_len = (long)(end_src - src);
+
+ mac->md_txt_off = (copy - tpl->td_text);
+ /*
+ * Copy the expression
+ */
+ memcpy(copy, src, src_len);
+ copy += src_len;
+ *(copy++) = NUL; *(copy++) = NUL; /* double terminate */
+
+ /*
+ * IF this expression has an "if-present" and "if-not-present"
+ * THEN find the ending expression...
+ */
+ if ((mac->md_res & EMIT_ALWAYS) != 0) {
+ char* pzNextExpr = (char*)skip_expr(pz, src_len);
+
+ /*
+ * The next expression must be within bounds and space separated
+ */
+ if (pzNextExpr >= pz + src_len)
+ AG_ABEND_IN(tpl, mac, LD_EXPR_NEED_TWO);
+
+ if (! IS_WHITESPACE_CHAR(*pzNextExpr))
+ AG_ABEND_IN(tpl, mac, LD_EXPR_NO_SPACE);
+
+ /*
+ * NUL terminate the first expression, skip intervening
+ * white space and put the secondary expression's type
+ * into the macro type code as the "secondary type".
+ */
+ *(pzNextExpr++) = NUL;
+ pzNextExpr = SPN_WHITESPACE_CHARS(pzNextExpr);
+ mac->md_res |= (expr_type(pzNextExpr) << EMIT_SECONDARY_SHIFT);
+ mac->md_end_idx = pzNextExpr - tpl->td_text;
+ }
+
+ mac->md_res |= expr_type(pz);
+ }
+
+ tpl->td_scan = copy;
+ return mac + 1;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/funcEval.c */
diff --git a/agen5/funcFor.c b/agen5/funcFor.c
new file mode 100644
index 0000000..2d6b219
--- /dev/null
+++ b/agen5/funcFor.c
@@ -0,0 +1,918 @@
+
+/**
+ * @file funcFor.c
+ *
+ * Time-stamp: "2012-04-07 09:13:53 bkorb"
+ *
+ * This module implements the FOR text macro.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define ENTRY_END INT_MAX
+#define UNASSIGNED 0x7BAD0BAD
+
+/* = = = START-STATIC-FORWARD = = = */
+static for_state_t *
+find_for_state(SCM which_scm);
+
+static bool
+next_def(bool invert, def_ent_t** de_lst);
+
+static int
+for_by_step(templ_t * pT, macro_t * pMac, def_ent_t * found);
+
+static int
+for_each(templ_t * tpl, macro_t * mac, def_ent_t * def_ent);
+
+static void
+load_for_in(char const * pzSrc, size_t srcLen, templ_t * pT, macro_t * pMac);
+/* = = = END-STATIC-FORWARD = = = */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Operational Functions */
+
+static for_state_t *
+find_for_state(SCM which_scm)
+{
+ ivk_info_t * srch = curr_ivk_info;
+ char const * which = AG_SCM_STRING_P(which_scm)
+ ? ag_scm2zchars(which_scm, "which for") : NULL;
+
+ for (; srch != NULL; srch = srch->ii_prev) {
+ int ix;
+
+ if (srch->ii_for_depth <= 0)
+ continue;
+
+ /*
+ * If "which" is not specified, then accept first found
+ */
+ ix = srch->ii_for_depth - 1;
+ if (which == NULL)
+ return srch->ii_for_data + ix;
+
+ for (; ix >= 0; ix--) {
+ for_state_t * fs = srch->ii_for_data + ix;
+ if (strcmp(fs->for_name, which) == 0)
+ return fs;
+ }
+ }
+ return NULL;
+}
+
+/*=gfunc first_for_p
+ *
+ * what: detect first iteration
+ * exparg: for_var, which for loop, opt
+ * doc:
+ *
+ * Returns @code{SCM_BOOL_T} if the named FOR loop (or, if not named, the
+ * current innermost loop) is on the first pass through the data. Outside
+ * of any @code{FOR} loop, it returns @code{SCM_UNDEFINED}, @pxref{FOR}.
+=*/
+SCM
+ag_scm_first_for_p(SCM which)
+{
+ for_state_t * p = find_for_state(which);
+ if (p == NULL)
+ return SCM_UNDEFINED;
+
+ if (! AG_SCM_STRING_P(which))
+ return (for_state->for_isfirst) ? SCM_BOOL_T : SCM_BOOL_F;
+
+ which = (p->for_isfirst ? SCM_BOOL_T : SCM_BOOL_F);
+ return which;
+}
+
+
+/*=gfunc last_for_p
+ *
+ * what: detect last iteration
+ * exparg: for_var, which for loop, opt
+ * doc: Returns SCM_BOOL_T if the named FOR loop (or, if not named, the
+ * current innermost loop) is on the last pass through the data.
+ * Outside of any FOR loop, it returns SCM_UNDEFINED.
+ * @xref{FOR}.
+=*/
+SCM
+ag_scm_last_for_p(SCM which)
+{
+ for_state_t * p = find_for_state(which);
+ if (p == NULL)
+ return SCM_UNDEFINED;
+
+ which = (p->for_islast ? SCM_BOOL_T : SCM_BOOL_F);
+ return which;
+}
+
+
+/*=gfunc for_index
+ *
+ * what: get current loop index
+ * exparg: for_var, which for loop, opt
+ * doc:
+ *
+ * Returns the current index for the named @code{FOR} loop.
+ * If not named, then the index for the innermost loop.
+ * Outside of any FOR loop, it returns @code{SCM_UNDEFINED}, @xref{FOR}.
+=*/
+SCM
+ag_scm_for_index(SCM which)
+{
+ for_state_t * p = find_for_state(which);
+ if (p == NULL)
+ return SCM_UNDEFINED;
+
+ which = AG_SCM_INT2SCM(p->for_index);
+ return which;
+}
+
+
+/*=gfunc for_from
+ *
+ * what: set initial index
+ * exparg: from, the initial index for the AutoGen FOR macro
+ *
+ * doc: This function records the initial index information
+ * for an AutoGen FOR function.
+ * Outside of the FOR macro itself, this function will emit an error.
+ * @xref{FOR}.
+=*/
+SCM
+ag_scm_for_from(SCM from)
+{
+ if ((! for_state->for_loading) || (! AG_SCM_NUM_P(from)))
+ return SCM_UNDEFINED;
+
+ for_state->for_from = AG_SCM_TO_INT(from);
+ return SCM_BOOL_T;
+}
+
+
+/*=gfunc for_to
+ *
+ * what: set ending index
+ * exparg: to, the final index for the AutoGen FOR macro
+ *
+ * doc: This function records the terminating value information
+ * for an AutoGen FOR function.
+ * Outside of the FOR macro itself, this function will emit an error.
+ * @xref{FOR}.
+=*/
+SCM
+ag_scm_for_to(SCM to)
+{
+ if ((! for_state->for_loading) || (! AG_SCM_NUM_P(to)))
+ return SCM_UNDEFINED;
+
+ for_state->for_to = AG_SCM_TO_INT(to);
+ return SCM_BOOL_T;
+}
+
+
+/*=gfunc for_by
+ *
+ * what: set iteration step
+ * exparg: by, the iteration increment for the AutoGen FOR macro
+ *
+ * doc: This function records the "step by" information
+ * for an AutoGen FOR function.
+ * Outside of the FOR macro itself, this function will emit an error.
+ * @xref{FOR}.
+=*/
+SCM
+ag_scm_for_by(SCM by)
+{
+ if ((! for_state->for_loading) || (! AG_SCM_NUM_P(by)))
+ return SCM_UNDEFINED;
+
+ for_state->for_by = AG_SCM_TO_INT(by);
+ return SCM_BOOL_T;
+}
+
+/*=gfunc for_sep
+ *
+ * what: set loop separation string
+ * exparg: separator, the text to insert between the output of
+ * each FOR iteration
+ *
+ * doc: This function records the separation string that is to be inserted
+ * between each iteration of an AutoGen FOR function. This is often
+ * nothing more than a comma.
+ * Outside of the FOR macro itself, this function will emit an error.
+=*/
+SCM
+ag_scm_for_sep(SCM obj)
+{
+ if ((! for_state->for_loading) || (! AG_SCM_STRING_P(obj)))
+ return SCM_UNDEFINED;
+
+ AGDUPSTR(for_state->for_sep_str, ag_scm2zchars(obj, "sep"), "seps");
+ return SCM_BOOL_T;
+}
+
+/**
+ * search for matching definition entry.
+ * Only the current level is traversed, via the "de_twin" link.
+ *
+ * @param[in] invert invert the sense of the search
+ * ("FOR" is running backwards)
+ * @param[in,out] de_lst linked list of definitions
+ */
+static bool
+next_def(bool invert, def_ent_t** de_lst)
+{
+ bool haveMatch = false;
+ def_ent_t* ent = *de_lst;
+
+ while (ent != NULL) {
+ /*
+ * Loop until we find or pass the current index value
+ *
+ * IF we found an entry for the current index,
+ * THEN break out and use it
+ */
+ if (ent->de_index == for_state->for_index) {
+ haveMatch = true;
+ break;
+ }
+
+ /*
+ * IF the next definition is beyond our current index,
+ * (that is, the current index is inside of a gap),
+ * THEN we have no current definition and will use
+ * only the set passed in.
+ */
+ if ((invert)
+ ? (ent->de_index < for_state->for_index)
+ : (ent->de_index > for_state->for_index)) {
+
+ /*
+ * When the "by" step is zero, force syncronization.
+ */
+ if (for_state->for_by == 0) {
+ for_state->for_index = ent->de_index;
+ haveMatch = true;
+ }
+ break;
+ }
+
+ /*
+ * The current index (for_state->for_index) is past the current value
+ * (pB->de_index), so advance to the next entry and test again.
+ */
+ ent = (invert) ? ent->de_ptwin : ent->de_twin;
+ }
+
+ /*
+ * Save our restart point and return the find indication
+ */
+ *de_lst = ent;
+ return haveMatch;
+}
+
+static int
+for_by_step(templ_t * pT, macro_t * pMac, def_ent_t * found)
+{
+ int loopCt = 0;
+ def_ent_t textDef;
+ bool invert = (for_state->for_by < 0) ? true : false;
+ t_word loopLimit = OPT_VALUE_LOOP_LIMIT;
+ def_ctx_t ctx = curr_def_ctx;
+ macro_t * end_mac = pT->td_macros + pMac->md_end_idx;
+
+ /*
+ * IF the for-from and for-to values have not been set,
+ * THEN we set them from the indices of the first and last
+ * entries of the twin set.
+ */
+ {
+ def_ent_t* pLast = (found->de_etwin != NULL)
+ ? found->de_etwin : found;
+
+ if (for_state->for_from == UNASSIGNED)
+ for_state->for_from = (invert) ? pLast->de_index : found->de_index;
+
+ if (for_state->for_to == UNASSIGNED)
+ for_state->for_to = (invert) ? found->de_index : pLast->de_index;
+
+ /*
+ * "loopLimit" is intended to catch runaway ending conditions.
+ * However, if you really have a gazillion entries, who am I
+ * to stop you?
+ */
+ if (loopLimit < pLast->de_index - found->de_index)
+ loopLimit = (pLast->de_index - found->de_index) + 1;
+ }
+
+ /*
+ * Make sure we have some work to do before we start.
+ */
+ if (invert) {
+ if (for_state->for_from < for_state->for_to)
+ return 0;
+ } else {
+ if (for_state->for_from > for_state->for_to)
+ return 0;
+ }
+
+ for_state->for_index = for_state->for_from;
+
+ /*
+ * FROM `from' THROUGH `to' BY `by',
+ * DO...
+ */
+ for (;;) {
+ int next_ix;
+ bool have_next = next_def(invert, &found);
+
+ if (loopLimit-- < 0) {
+ fprintf(trace_fp, TRACE_FOR_STEP_TOO_FAR,
+ pT->td_name, pMac->md_line);
+ fprintf(trace_fp, TRACE_FOR_BY_STEP,
+ pT->td_text + pMac->md_txt_off,
+ for_state->for_from, for_state->for_to, for_state->for_by,
+ (int)OPT_VALUE_LOOP_LIMIT);
+ break;
+ }
+
+ if (for_state->for_by != 0) {
+ next_ix = for_state->for_index + for_state->for_by;
+
+ } else if (invert) {
+ next_ix = (found->de_ptwin == NULL)
+ ? for_state->for_to - 1 /* last iteration !! */
+ : found->de_ptwin->de_index;
+
+ } else {
+ next_ix = (found->de_twin == NULL)
+ ? for_state->for_to + 1 /* last iteration !! */
+ : found->de_twin->de_index;
+ }
+
+ /*
+ * IF we have a non-base definition, use the old def context
+ */
+ if (! have_next)
+ curr_def_ctx = ctx;
+
+ /*
+ * ELSE IF this macro is a text type
+ * THEN create an un-twinned version of it to be found first
+ */
+ else if (found->de_type == VALTYP_TEXT) {
+ textDef = *found;
+ textDef.de_next = textDef.de_twin = NULL;
+
+ curr_def_ctx.dcx_defent = &textDef;
+ curr_def_ctx.dcx_prev = &ctx;
+ }
+
+ /*
+ * ELSE the current definitions are based on the block
+ * macro's values
+ */
+ else {
+ curr_def_ctx.dcx_defent = found->de_val.dvu_entry;
+ curr_def_ctx.dcx_prev = &ctx;
+ }
+
+ for_state->for_islast = (invert)
+ ? ((next_ix < for_state->for_to) ? true : false)
+ : ((next_ix > for_state->for_to) ? true : false);
+
+ switch (call_gen_block(for_state->for_env, pT, pMac+1, end_mac)) {
+ case LOOP_JMP_OKAY:
+ case LOOP_JMP_NEXT:
+ break;
+
+ case LOOP_JMP_BREAK:
+ goto leave_loop;
+ }
+
+ loopCt++;
+ for_state = curr_ivk_info->ii_for_data
+ + (curr_ivk_info->ii_for_depth - 1);
+
+ if (for_state->for_islast)
+ break;
+
+ if ( (for_state->for_sep_str != NULL)
+ && (*for_state->for_sep_str != NUL))
+ fputs(for_state->for_sep_str, cur_fpstack->stk_fp);
+
+ fflush(cur_fpstack->stk_fp);
+ for_state->for_isfirst = false;
+ for_state->for_index = next_ix;
+ } leave_loop:
+
+ curr_def_ctx = ctx; /* Restore the def context */
+ return loopCt;
+}
+
+static int
+for_each(templ_t * tpl, macro_t * mac, def_ent_t * def_ent)
+{
+ int loopCt = 0;
+ def_ctx_t ctx = curr_def_ctx;
+ macro_t * end_mac = tpl->td_macros + mac->md_end_idx;
+
+ curr_def_ctx.dcx_prev = &ctx;
+ mac++;
+
+ for (;;) {
+ def_ent_t txt_def_ent;
+
+ /*
+ * IF this loops over a text macro,
+ * THEN create a definition that will be found *before*
+ * the repeated (twinned) copy. That way, when it
+ * is found as a macro invocation, the current value
+ * will be extracted, instead of the value list.
+ */
+ if (def_ent->de_type == VALTYP_TEXT) {
+ txt_def_ent = *def_ent;
+ txt_def_ent.de_next = txt_def_ent.de_twin = NULL;
+
+ curr_def_ctx.dcx_defent = &txt_def_ent;
+ } else {
+ curr_def_ctx.dcx_defent = def_ent->de_val.dvu_entry;
+ }
+
+ /*
+ * Set the global current index
+ */
+ for_state->for_index = def_ent->de_index;
+
+ /*
+ * Advance to the next twin
+ */
+ def_ent = def_ent->de_twin;
+ if (def_ent == NULL)
+ for_state->for_islast = true;
+
+ switch (call_gen_block(for_state->for_env, tpl, mac, end_mac)) {
+ case LOOP_JMP_OKAY:
+ case LOOP_JMP_NEXT:
+ break;
+
+ case LOOP_JMP_BREAK:
+ goto leave_loop;
+ }
+
+ loopCt++;
+ for_state = curr_ivk_info->ii_for_data
+ + (curr_ivk_info->ii_for_depth - 1);
+
+ if (def_ent == NULL)
+ break;
+ for_state->for_isfirst = false;
+
+ /*
+ * Emit the iteration separation
+ */
+ fputs(for_state->for_sep_str, cur_fpstack->stk_fp);
+ fflush(cur_fpstack->stk_fp);
+ } leave_loop:;
+
+ curr_def_ctx = ctx; /* Restore the def context */
+ return loopCt;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void
+load_for_in(char const * pzSrc, size_t srcLen, templ_t * pT, macro_t * pMac)
+{
+ char* pzName = pT->td_text + pMac->md_name_off;
+ int ix = 0;
+ char* pz;
+ def_ent_t * prev_ent = NULL;
+
+ /*
+ * Find the first text value
+ */
+ pz = SPN_WHITESPACE_CHARS(pzSrc + 3);
+ if (*pz == NUL)
+ AG_ABEND_IN(pT, pMac, FOR_IN_LISTLESS);
+ srcLen -= pz - pzSrc;
+ pzSrc = pz;
+
+ {
+ size_t nmlen = strlen(pzName);
+
+ pz = AGALOC(srcLen + 2 + nmlen, "copy FOR text");
+ strcpy(pz, pzName);
+ pzName = pz;
+ pz += nmlen + 1;
+ }
+
+ memcpy(pz, pzSrc, srcLen);
+ pz[srcLen] = NUL;
+
+ do {
+ def_ent_t* pDef = new_def_ent();
+
+ pDef->de_name = pzName;
+ pDef->de_index = ix++;
+ pDef->de_type = VALTYP_TEXT;
+ pDef->de_val.dvu_text = pz;
+
+ switch (*pz) {
+ case '\'':
+ case '"':
+ pz = span_quote(pz);
+ /*
+ * Clean up trailing commas
+ */
+ pz = SPN_WHITESPACE_CHARS(pz);
+ if (*pz == ',')
+ pz++;
+ break;
+
+ default:
+ for (;;) {
+ char ch = *(pz++);
+ switch (ch) {
+ case ' ': case TAB: case '\f': case '\v': case NL:
+ pz[-1] = NUL;
+ if (*pz != ',')
+ break;
+ pz++;
+ /* FALLTHROUGH */
+
+ case ',':
+ pz[-1] = NUL;
+ break;
+
+ case NUL:
+ pz--;
+ break;
+
+ default:
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+
+ /*
+ * Clean up trailing white space
+ */
+ pz = SPN_WHITESPACE_CHARS(pz);
+
+ /*
+ * IF there is a previous entry, link its twin to this one.
+ * OTHERWISE, it is the head of the twin list.
+ * Link to md_pvt.
+ */
+ if (prev_ent != NULL)
+ prev_ent->de_twin = pDef;
+ else
+ pMac->md_pvt = pDef;
+
+ prev_ent = pDef;
+ } while (*pz != NUL);
+
+ pMac->md_txt_off = 0;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*=macfunc FOR
+ *
+ * what: Emit a template block multiple times
+ * cindex: looping, for
+ * cindex: for loop
+ * handler_proc:
+ * load_proc:
+ *
+ * desc:
+ * This macro has a slight variation on the standard syntax:
+ * @example
+ * FOR <value-name> [ <separator-string> ]
+ *
+ * FOR <value-name> (...Scheme expression list)
+ *
+ * FOR <value-name> IN "string" [ ... ]
+ * @end example
+ *
+ * Other than for the last form, the first macro argument must be the name of
+ * an AutoGen value. If there is no value associated with the name, the
+ * @code{FOR} template block is skipped entirely. The scope of the @code{FOR}
+ * macro extends to the corresponding @code{ENDFOR} macro. The last form will
+ * create an array of string values named @code{<value-name>} that only exists
+ * within the context of this @code{FOR} loop. With this form, in order to
+ * use a @code{separator-string}, you must code it into the end of the
+ * template block using the @code{(last-for?)} predicate function
+ * (@pxref{SCM last-for?}).
+ *
+ * If there are any arguments after the @code{value-name}, the initial
+ * characters are used to determine the form. If the first character is
+ * either a semi-colon (@code{;}) or an opening parenthesis (@code{(}), then
+ * it is presumed to be a Scheme expression containing the FOR macro specific
+ * functions @code{for-from}, @code{for-by}, @code{for-to}, and/or
+ * @code{for-sep}. @xref{AutoGen Functions}. If it consists of an '@code{i}'
+ * an '@code{n}' and separated by white space from more text, then the
+ * @code{FOR x IN} form is processed. Otherwise, the remaining text is
+ * presumed to be a string for inserting between each iteration of the loop.
+ * This string will be emitted one time less than the number of iterations of
+ * the loop. That is, it is emitted after each loop, excepting for the last
+ * iteration.
+ *
+ * If the from/by/to functions are invoked, they will specify which copies of
+ * the named value are to be processed. If there is no copy of the named
+ * value associated with a particular index, the @code{FOR} template block
+ * will be instantiated anyway. The template must use methods for detecting
+ * missing definitions and emitting default text. In this fashion, you can
+ * insert entries from a sparse or non-zero based array into a dense, zero
+ * based array.
+ *
+ * @strong{NB:} the @code{for-from}, @code{for-to}, @code{for-by} and
+ * @code{for-sep} functions are disabled outside of the context of the
+ * @code{FOR} macro. Likewise, the @code{first-for}, @code{last-for}
+ * and @code{for-index} functions are disabled outside of the range
+ * of a @code{FOR} block.
+ *
+ * @strong{Also:} the @code{<value-name>} must be a single level name,
+ * not a compound name (@pxref{naming values}).
+ *
+ * @example
+ * [+FOR var (for-from 0) (for-to <number>) (for-sep ",") +]
+ * ... text with @code{var}ious substitutions ...[+
+ * ENDFOR var+]
+ * @end example
+ *
+ * @noindent
+ * this will repeat the @code{... text with @code{var}ious
+ * substitutions ...} <number>+1 times. Each repetition,
+ * except for the last, will have a comma @code{,} after it.
+ *
+ * @example
+ * [+FOR var ",\n" +]
+ * ... text with @code{var}ious substitutions ...[+
+ * ENDFOR var +]
+ * @end example
+ *
+ * @noindent
+ * This will do the same thing, but only for the index
+ * values of @code{var} that have actually been defined.
+=*/
+/*=macfunc ENDFOR
+ *
+ * what: Terminates the @code{FOR} function template block
+ * in-context:
+ *
+ * desc:
+ * This macro ends the @code{FOR} function template block.
+ * For a complete description @xref{FOR}.
+=*/
+macro_t *
+mFunc_For(templ_t * tpl, macro_t * mac)
+{
+ macro_t * ret_mac = tpl->td_macros + mac->md_end_idx;
+ def_ent_t * def;
+ int lp_ct;
+
+ /*
+ * "md_pvt" is used by the "FOR x IN ..." variation of the macro.
+ * When parsed, a chain of text definitions are hung off of "md_pvt".
+ * "for_each()" will then chase through the linked list of text
+ * values. This winds up being identical to [+ FOR var +] where
+ * a list of "var" values has been set.
+ */
+ if (mac->md_pvt != NULL)
+ def = mac->md_pvt;
+ else {
+ bool indexed;
+ def = find_def_ent(tpl->td_text + mac->md_name_off, &indexed);
+ if (def == NULL) {
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) {
+ fprintf(trace_fp, TRACE_FN_FOR_SKIP,
+ tpl->td_text + mac->md_name_off);
+
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT,
+ tpl->td_file, mac->md_line);
+ }
+
+ return ret_mac;
+ }
+ }
+
+ for_state = new_for_context();
+ for_state->for_name = tpl->td_text + mac->md_name_off;
+
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS)
+ fprintf(trace_fp, TRACE_FN_FOR, tpl->td_text + mac->md_name_off,
+ tpl->td_file, mac->md_line);
+
+ /*
+ * Check for a FOR iterating based on scheme macros
+ */
+ if (tpl->td_text[ mac->md_txt_off ] == '(') {
+ for_state->for_from = \
+ for_state->for_to = UNASSIGNED;
+
+ for_state->for_loading = true;
+ (void) eval(tpl->td_text + mac->md_txt_off);
+ for_state->for_loading = false;
+ lp_ct = for_by_step(tpl, mac, def);
+
+ } else {
+ /*
+ * The FOR iterates over a list of definitions
+ */
+ AGDUPSTR(for_state->for_sep_str, tpl->td_text + mac->md_txt_off, "ss");
+ lp_ct = for_each(tpl, mac, def);
+ }
+
+ if (for_state->for_sep_str != NULL)
+ AGFREE(for_state->for_sep_str);
+
+ free_for_context(false);
+
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) {
+ fprintf(trace_fp, TRACE_FN_FOR_REPEAT, tpl->td_text + mac->md_name_off,
+ lp_ct);
+
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT, tpl->td_file, mac->md_line);
+ }
+
+ return ret_mac;
+}
+
+/**
+ * allocate and zero out a FOR macro context.
+ *
+ * @returns the top of thecurr_ivk_info stack of contexts.
+ */
+LOCAL for_state_t *
+new_for_context(void)
+{
+ for_state_t * res;
+
+ /*
+ * Push the current FOR context onto a stack.
+ */
+ if (++(curr_ivk_info->ii_for_depth) > curr_ivk_info->ii_for_alloc) {
+ void * dta = curr_ivk_info->ii_for_data;
+ size_t sz = (curr_ivk_info->ii_for_alloc += 5) * sizeof(for_state_t);
+
+ if (dta == NULL)
+ dta = AGALOC(sz, "Init FOR sate");
+ else dta = AGREALOC(dta, sz, "FOR state");
+ curr_ivk_info->ii_for_data = dta;
+ }
+
+ res = curr_ivk_info->ii_for_data + (curr_ivk_info->ii_for_depth - 1);
+ memset((void*)res, 0, sizeof(for_state_t));
+ res->for_isfirst = true;
+ return res;
+}
+
+/**
+ * allocate and zero out a FOR macro context.
+ *
+ * @returns the top of thecurr_ivk_info stack of contexts.
+ */
+LOCAL void
+free_for_context(bool everything)
+{
+ if ((--(curr_ivk_info->ii_for_depth) == 0) || everything) {
+ AGFREE(curr_ivk_info->ii_for_data);
+ curr_ivk_info->ii_for_data = NULL;
+ curr_ivk_info->ii_for_alloc = 0;
+ curr_ivk_info->ii_for_depth = 0;
+ }
+}
+
+/**
+ * Function to initiate loading of FOR block.
+ * It must replace the dispatch table to handle the ENDFOR function,
+ * which will unload the dispatch table.
+ */
+macro_t *
+mLoad_For(templ_t * tpl, macro_t * mac, char const ** p_scan)
+{
+ char * scan_out = tpl->td_scan; /* next text dest */
+ char const * scan_in = (char const*)mac->md_txt_off; /* macro text */
+ size_t in_len = (size_t)mac->md_res; /* macro len */
+
+ /*
+ * Save the global macro loading mode
+ */
+ load_proc_p_t const * save_load_procs = load_proc_table;
+
+ static load_proc_p_t load_for_macro_procs[ FUNC_CT ] = { NULL };
+
+ load_proc_table = load_for_macro_procs;
+
+ /*
+ * IF this is the first time here, THEN set up the "FOR" mode callout
+ * table. It is the standard table, except entries are inserted for
+ * functions that are enabled only while processing a FOR macro
+ */
+ if (load_for_macro_procs[0] == NULL) {
+ memcpy((void*)load_for_macro_procs, base_load_table,
+ sizeof(base_load_table));
+ load_for_macro_procs[ FTYP_ENDFOR ] = &mLoad_Ending;
+ }
+
+ if (in_len == 0)
+ AG_ABEND_IN(tpl, mac, LD_FOR_NAMELESS);
+
+ /*
+ * src points to the name of the iteration "variable"
+ * Special hack: if the name is preceeded by a `.',
+ * then the lookup is local-only and we will accept it.
+ */
+ mac->md_name_off = tpl->td_scan - tpl->td_text;
+ if (*scan_in == '.') {
+ *(scan_out++) = *(scan_in++);
+ if (! IS_VAR_FIRST_CHAR(*scan_in))
+ scan_out--; /* force an error */
+ }
+
+ while (IS_VALUE_NAME_CHAR(*scan_in)) *(scan_out++) = *(scan_in++);
+ *(scan_out++) = NUL;
+
+ if (tpl->td_text[ mac->md_name_off ] == NUL)
+ AG_ABEND_IN(tpl, mac, LD_FOR_INVALID_VAR);
+
+ /*
+ * Skip space to the start of the text following the iterator name
+ */
+ scan_in = SPN_WHITESPACE_CHARS(scan_in);
+ in_len -= scan_in - (char*)mac->md_txt_off;
+
+ /* * * * *
+ *
+ * No source -> zero offset to text
+ */
+ if ((ssize_t)in_len <= 0) {
+ mac->md_txt_off = 0;
+ }
+
+ /* * * * *
+ *
+ * FOR foo IN ... -> no text, but we create an array of text values
+ */
+ else if ( (strneqvcmp(scan_in, LD_FOR_IN, 2) == 0)
+ && IS_WHITESPACE_CHAR(scan_in[2])) {
+ load_for_in(scan_in, in_len, tpl, mac);
+ }
+
+ /* * * * *
+ *
+ * *EITHER* a: FOR foo "<<separator>>"
+ * *OR* FOR foo (scheme ...) ...
+ */
+ else {
+ mac->md_txt_off = scan_out - tpl->td_text;
+ memmove(scan_out, scan_in, in_len);
+ scan_out[in_len] = NUL;
+ if (IS_QUOTE_CHAR(*scan_out))
+ span_quote(scan_out);
+ scan_out += in_len + 1;
+ }
+ /*
+ * * * * */
+
+ tpl->td_scan = scan_out;
+ {
+ macro_t * next_mac = parse_tpl(mac + 1, p_scan);
+ if (*p_scan == NULL)
+ AG_ABEND_IN(tpl, mac, LD_FOR_NO_ENDFOR);
+
+ mac->md_end_idx = mac->md_sib_idx = next_mac - tpl->td_macros;
+
+ load_proc_table = save_load_procs;
+ return next_mac;
+ }
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/funcFor.c */
diff --git a/agen5/funcIf.c b/agen5/funcIf.c
new file mode 100644
index 0000000..ddf3aa9
--- /dev/null
+++ b/agen5/funcIf.c
@@ -0,0 +1,514 @@
+
+/**
+ * @file funcIf.c
+ *
+ * Time-stamp: "2012-04-07 09:59:54 bkorb"
+ *
+ * This module implements the _IF text function.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef struct if_stack tIfStack;
+struct if_stack {
+ macro_t* pIf;
+ macro_t* pElse;
+};
+
+static tIfStack current_if;
+static load_proc_t mLoad_Elif, mLoad_Else;
+
+/* = = = START-STATIC-FORWARD = = = */
+static bool
+eval_true(void);
+
+static macro_t*
+mLoad_Elif(templ_t * pT, macro_t * pMac, char const ** ppzScan);
+
+static macro_t *
+mLoad_Else(templ_t * pT, macro_t * pMac, char const ** ppzScan);
+/* = = = END-STATIC-FORWARD = = = */
+
+/*
+ * eval_true - should a string be interpreted as TRUE?
+ *
+ * It is always true unless:
+ *
+ * 1. it is the empty string
+ * 2. it starts with a digit and the number evaluates to zero
+ * 3. it starts with either "#f" or "#F"
+ * 4. For its length or its first five characters (whichever is less)
+ * it matches the string "false"
+ */
+static bool
+eval_true(void)
+{
+ bool needFree;
+ bool res = true;
+ char const * pz = eval_mac_expr(&needFree);
+
+ if (IS_DEC_DIGIT_CHAR(*pz))
+ res = (atoi(pz) == 0) ? false : true;
+
+ else switch (*pz) {
+ case NUL:
+ res = false;
+ break;
+
+ case '#':
+ if ((pz[1] == 'f') || (pz[1] == 'F'))
+ res = false;
+ break;
+
+ case 'f':
+ case 'F':
+ {
+ int len = strlen(pz);
+ if (len > 5)
+ len = 5;
+ if (strneqvcmp(EVAL_TRUE_FALSE_STR, pz, len) == 0)
+ res = false;
+ break;
+ }
+ }
+
+ if (needFree)
+ AGFREE(pz);
+
+ return res;
+}
+
+
+/*=macfunc IF
+ *
+ * what: Conditionally Emit a Template Block
+ * cindex: conditional emit
+ * cindex: if test
+ * handler_proc:
+ * load_proc:
+ *
+ * desc:
+ * Conditional block. Its arguments are evaluated (@pxref{EXPR}) and
+ * if the result is non-zero or a string with one or more bytes,
+ * then the condition is true and the text from that point
+ * until a matched @code{ELIF}, @code{ELSE} or @code{ENDIF} is emitted.
+ * @code{ELIF} introduces a conditional alternative if the @code{IF}
+ * clause evaluated FALSE and @code{ELSE} introduces an unconditional
+ * alternative.
+ *
+ * @example
+ * [+IF <full-expression> +]
+ * emit things that are for the true condition[+
+ *
+ * ELIF <full-expression-2> +]
+ * emit things that are true maybe[+
+ *
+ * ELSE "This may be a comment" +]
+ * emit this if all but else fails[+
+ *
+ * ENDIF "This may *also* be a comment" +]
+ * @end example
+ *
+ * @noindent
+ * @code{<full-expression>} may be any expression described in the
+ * @code{EXPR} expression function, including the use of apply-codes
+ * and value-names. If the expression yields an empty string, it
+ * is interpreted as @i{false}.
+=*/
+/*=macfunc ENDIF
+ *
+ * what: Terminate the @code{IF} Template Block
+ * in-context:
+ *
+ * desc:
+ * This macro ends the @code{IF} function template block.
+ * For a complete description @xref{IF}.
+=*/
+macro_t*
+mFunc_If(templ_t* pT, macro_t* pMac)
+{
+ macro_t* pRet = pT->td_macros + pMac->md_end_idx;
+ macro_t* pIf = pMac;
+
+ do {
+ /*
+ * The current macro becomes the 'ELIF' or 'ELSE' macro
+ */
+ cur_macro = pMac;
+
+ /*
+ * 'ELSE' is equivalent to 'ELIF true'
+ */
+ if ( (pMac->md_code == FTYP_ELSE)
+ || eval_true()) {
+
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) {
+ fprintf(trace_fp, TRACE_FN_IF, (pMac->md_code == FTYP_ELSE)
+ ? FN_IF_ELSE : pT->td_text + pMac->md_txt_off,
+ pMac->md_line);
+
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT,
+ current_tpl->td_file, pIf->md_line);
+ }
+
+ gen_block(pT, pMac+1, pT->td_macros + pMac->md_sib_idx);
+ break;
+ }
+ pMac = pT->td_macros + pMac->md_sib_idx;
+ } while (pMac < pRet);
+
+ if ((OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) && (pMac >= pRet)) {
+ fprintf(trace_fp, TRACE_FN_IF_NOTHING,
+ current_tpl->td_text + cur_macro->md_txt_off);
+
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT,
+ current_tpl->td_file, pIf->md_line);
+ }
+
+ return pRet;
+}
+
+
+/*=macfunc WHILE
+ *
+ * what: Conditionally loop over a Template Block
+ * cindex: conditional emit
+ * cindex: while test
+ * handler_proc:
+ * load_proc:
+ *
+ * desc:
+ * Conditionally repeated block. Its arguments are evaluated (@pxref{EXPR})
+ * and as long as the result is non-zero or a string with one or more bytes,
+ * then the condition is true and the text from that point
+ * until a matched @code{ENDWHILE} is emitted.
+ *
+ * @example
+ * [+WHILE <full-expression> +]
+ * emit things that are for the true condition[+
+ *
+ * ENDWHILE +]
+ * @end example
+ *
+ * @noindent
+ * @code{<full-expression>} may be any expression described in the
+ * @code{EXPR} expression function, including the use of apply-codes
+ * and value-names. If the expression yields an empty string, it
+ * is interpreted as @i{false}.
+=*/
+/*=macfunc ENDWHILE
+ *
+ * what: Terminate the @code{WHILE} Template Block
+ * in-context:
+ *
+ * desc:
+ * This macro ends the @code{WHILE} function template block.
+ * For a complete description @xref{WHILE}.
+=*/
+macro_t*
+mFunc_While(templ_t* pT, macro_t* pMac)
+{
+ macro_t * end = pT->td_macros + pMac->md_end_idx;
+ int ct = 0;
+
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS)
+ fprintf(trace_fp, TRACE_FN_WHILE_START,
+ current_tpl->td_text + cur_macro->md_txt_off,
+ pT->td_file, pMac->md_line);
+
+ for (;;) {
+ jmp_buf jbuf;
+
+ current_tpl = pT;
+ cur_macro = pMac;
+
+ if (! eval_true())
+ break;
+ ct++;
+ if (call_gen_block(jbuf, pT, pMac+1, end) == LOOP_JMP_BREAK)
+ break;
+ }
+
+ if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) {
+ fprintf(trace_fp, TRACE_FN_WHILE_END, ct);
+
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TAB_FILE_LINE_FMT, pT->td_file, pMac->md_line);
+ }
+
+ return end;
+}
+
+/*=macfunc ELIF
+ *
+ * what: Alternate Conditional Template Block
+ * in-context:
+ *
+ * desc:
+ * This macro must only appear after an @code{IF} function, and
+ * before any associated @code{ELSE} or @code{ENDIF} functions.
+ * It denotes the start of an alternate template block for the
+ * @code{IF} function. Its expression argument is evaluated as are
+ * the arguments to @code{IF}. For a complete description @xref{IF}.
+=*/
+static macro_t*
+mLoad_Elif(templ_t * pT, macro_t * pMac, char const ** ppzScan)
+{
+ if ((int)pMac->md_res == 0)
+ AG_ABEND_IN(pT, pMac, NO_IF_EXPR);
+ /*
+ * Load the expression
+ */
+ (void)mLoad_Expr(pT, pMac, ppzScan);
+
+ current_if.pElse->md_sib_idx = pMac - pT->td_macros;
+ current_if.pElse = pMac;
+ return pMac + 1;
+}
+
+
+/*=macfunc ELSE
+ *
+ * what: Alternate Template Block
+ * in-context:
+ *
+ * desc:
+ * This macro must only appear after an @code{IF} function,
+ * and before the associated @code{ENDIF} function.
+ * It denotes the start of an alternate template block for
+ * the @code{IF} function. For a complete description @xref{IF}.
+=*/
+static macro_t *
+mLoad_Else(templ_t * pT, macro_t * pMac, char const ** ppzScan)
+{
+ /*
+ * After processing an "ELSE" macro,
+ * we have a special handler function for 'ENDIF' only.
+ */
+ static load_proc_p_t load_for_if_after_else_procs[ FUNC_CT ] = { NULL };
+ (void)ppzScan;
+
+ if (load_for_if_after_else_procs[0] == NULL) {
+ memcpy((void*)load_for_if_after_else_procs, base_load_table,
+ sizeof(base_load_table));
+ load_for_if_after_else_procs[ FTYP_ENDIF ] = &mLoad_Ending;
+ }
+
+ load_proc_table = load_for_if_after_else_procs;
+
+ current_if.pElse->md_sib_idx = pMac - pT->td_macros;
+ current_if.pElse = pMac;
+ pMac->md_txt_off = 0;
+
+ return pMac+1;
+}
+
+
+/**
+ * End any of the block macros. It ends @code{ENDDEF},
+ * @code{ENDFOR}, @code{ENDIF}, @code{ENDWHILE} and @code{ESAC}. It
+ * leaves no entry in the dispatch tables for itself. By returning
+ * NULL, it tells the macro parsing loop to return.
+ *
+ * @param tpl ignored
+ * @param[out] mac zeroed out for re-use
+ * @param scan ignored
+ */
+macro_t *
+mLoad_Ending(templ_t * tpl, macro_t * mac, char const ** scan)
+{
+ (void) tpl;
+ (void) scan;
+ memset((void*)mac, 0, sizeof(*mac));
+ return NULL;
+}
+
+/**
+ * Load template macros until matching @code{ENDIF} is found.
+ *
+ * @param[in,out] tpl Template we are filling in with macros
+ * @param[in,out] mac Linked into the "if" macro segments
+ * @param[in,out] scan pointer to scanning pointer. We advance it
+ * past the ending @code{ENDIF} macro.
+ *
+ * @returns the address of the next macro slot for insertion.
+ */
+macro_t *
+mLoad_If(templ_t * tpl, macro_t * mac, char const ** ppzScan)
+{
+ size_t srcLen = (size_t)mac->md_res; /* macro len */
+ tIfStack save_stack = current_if;
+ load_proc_p_t const * papLP = load_proc_table;
+ macro_t * pEndifMac;
+
+ /*
+ * While processing an "IF" macro,
+ * we have handler functions for 'ELIF', 'ELSE' and 'ENDIF'
+ * Otherwise, we do not. Switch the callout function table.
+ */
+ static load_proc_p_t apIfLoad[ FUNC_CT ] = { NULL };
+
+ /*
+ * IF there is no associated text expression
+ * THEN woops! what are we to case on?
+ */
+ if (srcLen == 0)
+ AG_ABEND_IN(tpl, mac, NO_IF_EXPR);
+
+ if (apIfLoad[0] == NULL) {
+ memcpy((void*)apIfLoad, base_load_table, sizeof(base_load_table));
+ apIfLoad[ FTYP_ELIF ] = &mLoad_Elif;
+ apIfLoad[ FTYP_ELSE ] = &mLoad_Else;
+ apIfLoad[ FTYP_ENDIF ] = &mLoad_Ending;
+ }
+
+ load_proc_table = apIfLoad;
+
+ /*
+ * We will need to chain together the 'IF', 'ELIF', and 'ELSE'
+ * macros. The 'ENDIF' gets absorbed.
+ */
+ current_if.pIf = current_if.pElse = mac;
+
+ /*
+ * Load the expression
+ */
+ (void)mLoad_Expr(tpl, mac, ppzScan);
+
+ /*
+ * Now, do a nested parse of the template.
+ * When the matching 'ENDIF' macro is encountered,
+ * the handler routine will cause 'parse_tpl()'
+ * to return with the text scanning pointer pointing
+ * to the remaining text.
+ */
+ pEndifMac = parse_tpl(mac+1, ppzScan);
+ if (*ppzScan == NULL)
+ AG_ABEND_IN(tpl, mac, LD_IF_NO_ENDIF);
+
+ current_if.pIf->md_end_idx = \
+ current_if.pElse->md_sib_idx = pEndifMac - tpl->td_macros;
+
+ /*
+ * Restore the context of any encompassing block macros
+ */
+ current_if = save_stack;
+ load_proc_table = papLP;
+ return pEndifMac;
+}
+
+/**
+ * load the @code{WHILE} macro. Sets up the while loop parsing table, which
+ * is a copy of the global "base_load_table" with added entries for
+ * @code{ENDWHILE}, @code{NEXT} and @code{BREAK}.
+ *
+ * @param[in,out] tpl Template we are filling in with macros
+ * @param[in,out] mac Linked into the "if" macro segments
+ * @param[in,out] scan pointer to scanning pointer. We advance it
+ * past the ending @code{ENDWHILE} macro.
+ *
+ * @returns the address of the next macro slot for insertion.
+ */
+macro_t *
+mLoad_While(templ_t * pT, macro_t * mac, char const ** p_scan)
+{
+ /*
+ * While processing a "WHILE" macro,
+ * we have handler a handler function for ENDWHILE, NEXT and BREAK.
+ */
+ static load_proc_p_t while_tbl[ FUNC_CT ] = { NULL };
+
+
+ load_proc_p_t const * lpt = load_proc_table; //!< save current table
+
+ /*
+ * IF there is no associated text expression
+ * THEN woops! what are we to case on?
+ */
+ if ((size_t)mac->md_res == 0)
+ AG_ABEND_IN(pT, mac, LD_WHILE_NO_EXPR);
+
+ if (while_tbl[0] == NULL) {
+ memcpy((void*)while_tbl, base_load_table, sizeof(base_load_table));
+ while_tbl[ FTYP_ENDWHILE ] = &mLoad_Ending;
+ }
+
+ load_proc_table = while_tbl;
+
+ /*
+ * Load the expression
+ */
+ (void)mLoad_Expr(pT, mac, p_scan);
+
+ /*
+ * Now, do a nested parse of the template. When the matching 'ENDWHILE'
+ * macro is encountered, the handler routine will cause 'parse_tpl()'
+ * to return with the text scanning pointer pointing to the remaining
+ * text.
+ */
+ {
+ macro_t * end = parse_tpl(mac+1, p_scan);
+ if (*p_scan == NULL)
+ AG_ABEND_IN(pT, mac, LD_WHILE_NO_ENDWHILE);
+
+ mac->md_sib_idx = \
+ mac->md_end_idx = end - pT->td_macros;
+
+ load_proc_table = lpt; // restore context
+ return end;
+ }
+}
+
+/*=gfunc set_writable
+ *
+ * what: Make the output file be writable
+ *
+ * exparg: + set? + boolean arg, false to make output non-writable + opt
+ *
+ * doc: This function will set the current output file to be writable
+ * (or not). This is only effective if neither the @code{--writable}
+ * nor @code{--not-writable} have been specified. This state
+ * is reset when the current suffix's output is complete.
+=*/
+SCM
+ag_scm_set_writable(SCM set)
+{
+ switch (STATE_OPT(WRITABLE)) {
+ case OPTST_DEFINED:
+ case OPTST_PRESET:
+ fprintf(trace_fp, SET_WRITE_WARN, current_tpl->td_file,
+ cur_macro->md_line);
+ break;
+
+ default:
+ if (AG_SCM_BOOL_P(set) && (set == SCM_BOOL_F))
+ CLEAR_OPT(WRITABLE);
+ else
+ SET_OPT_WRITABLE;
+ }
+
+ return SCM_UNDEFINED;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/funcIf.c */
diff --git a/agen5/functions.c b/agen5/functions.c
new file mode 100644
index 0000000..963bd06
--- /dev/null
+++ b/agen5/functions.c
@@ -0,0 +1,539 @@
+
+/**
+ * @file functions.c
+ *
+ * Time-stamp: "2012-06-10 11:24:45 bkorb"
+ *
+ * This module implements text functions.
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*=macfunc CONTINUE
+ *
+ * handler-proc: Break
+ * load-proc: Leave
+ * what: Skip to end of a FOR or WHILE macro.
+ *
+ * desc:
+ * This will skip the remainder of the loop and start the next.
+=*/
+
+/*=macfunc BREAK
+ *
+ * handler-proc: Break
+ * load-proc: Leave
+ * what: Leave a FOR or WHILE macro
+ *
+ * desc:
+ * This will unwind the loop context and resume after ENDFOR/ENDWHILE.
+ * Note that unless this happens to be the last iteration anyway,
+ * the (last-for?) function will never yield "#t".
+=*/
+macro_t *
+mFunc_Break(templ_t * tpl, macro_t * mac)
+{
+ for_state_t * fst = curr_ivk_info->ii_for_data;
+ int code = (mac->md_code == FTYP_BREAK) ? LOOP_JMP_BREAK : LOOP_JMP_NEXT;
+ if (fst == NULL) {
+ char const * which =
+ (mac->md_code == FTYP_BREAK) ? BREAK_STR : CONTINUE_STR;
+ AG_ABEND(aprf(BAD_BREAK_FMT, which));
+ }
+ fst += curr_ivk_info->ii_for_depth - 1;
+
+ (void)tpl;
+ (void)mac;
+ longjmp(fst->for_env, code);
+}
+
+/**
+ * wrapper function for calling gen_block in a loop.
+ * It sets up and handles the jump buffer, returning the jump result.
+ *
+ * @param[in,out] jbuf the jump buffer
+ * @param[in] tpl the new active template
+ * @param[in] mac the looping macro
+ * @param[in] end_mac pointer to the first macro after the block
+ *
+ * @returns either LOOP_JMP_OKAY (0) or LOOP_JMP_BREAK (the caller should
+ * exit the loop).
+ */
+LOCAL loop_jmp_type_t
+call_gen_block(jmp_buf jbuf, templ_t * tpl, macro_t * mac, macro_t * end_mac)
+{
+ switch (setjmp(jbuf)) {
+ case LOOP_JMP_OKAY: // 0
+ gen_block(tpl, mac, end_mac);
+ /* FALLTHROUGH */
+
+ case LOOP_JMP_NEXT:
+ return LOOP_JMP_OKAY;
+
+ case LOOP_JMP_BREAK:
+ default:
+ return LOOP_JMP_BREAK;
+ }
+}
+
+/*=macfunc RETURN
+ *
+ * handler-proc:
+ * load-proc: Leave
+ *
+ * what: Leave an INVOKE-d (DEFINE) macro
+ *
+ * desc:
+ * This will unwind looping constructs inside of a DEFINE-d macro and
+ * return to the invocation point. The output files and diversions
+ * @i{are left alone}. This means it is unwise to start diversions
+ * in a DEFINEd macro and RETURN from it before you have handled the
+ * diversion. Unless you are careful. Here is some rope for you.
+ * Please be careful using it.
+=*/
+macro_t *
+mFunc_Return(templ_t * tpl, macro_t * mac)
+{
+ (void)tpl;
+ (void)mac;
+ free_for_context(true);
+ if (curr_ivk_info->ii_prev == NULL)
+ AG_ABEND_IN(tpl, mac, RETURN_FROM_NOWHERE);
+ longjmp(curr_ivk_info->ii_env, 1);
+}
+
+/**
+ * Generate a block with a new template context. It may be either
+ * an @code{INCLUDE}-d template or a user @code{DEFINE}-d macro.
+ * If @code{gen_block} returns with a long jump, the long jump value
+ * is ignored. It was terminated early with a @code{RETURN}.
+ *
+ * @param[in] tpl new template block (included or invoked).
+ */
+LOCAL void
+gen_new_block(templ_t * tpl)
+{
+ templ_t * oldt = current_tpl;
+ ivk_info_t ii = IVK_INFO_INITIALIZER(curr_ivk_info);
+
+ curr_ivk_info = &ii;
+
+ if (setjmp(ii.ii_env) == 0) {
+ macro_t * m = tpl->td_macros;
+ gen_block(tpl, m, m + tpl->td_mac_ct);
+ }
+
+ current_tpl = oldt;
+ curr_ivk_info = ii.ii_prev;
+}
+
+/**
+ * Validate the context for leaving early. @code{FOR} and @code{WHILE} loops
+ * may leave an interation early with @code{CONTINUE} or @code{BREAK}.
+ * @code{DEFINE}-d macros and @code{INCLUDE}-d files may leave early with
+ * @code{RETURN}. Loops may not be left early from an @code{INVOKE}-d macro
+ * or an @code{INCLUDE}-d template.
+ *
+ * This load function handles @code{BREAK}, @code{CONTINUE} and @code{RETURN}.
+ * It is always defined, so it must check for itself whether the
+ * context is correct or not.
+ *
+ * @param tpl template being loaded
+ * @param mac the macro descriptor
+ * @param p_scan the input text scanning pointer
+ *
+ * @returns the macro table entry after mac
+ */
+LOCAL macro_t *
+mLoad_Leave(templ_t * tpl, macro_t * mac, char const ** p_scan)
+{
+ (void) tpl;
+
+ if (mac->md_code == FTYP_RETURN) {
+ /*
+ * Check returns at load time. "break" and "continue" at
+ * instantiation time.
+ */
+ if (! defining_macro && (include_depth == 0))
+ (void)mLoad_Bogus(tpl, mac, p_scan);
+ }
+ return mac + 1;
+}
+
+/*=macfunc INCLUDE
+ *
+ * what: Read in and emit a template block
+ * handler_proc:
+ * load_proc: Expr
+ *
+ * desc:
+ *
+ * The entire contents of the named file is inserted at this point.
+ * The contents of the file are processed for macro expansion. The
+ * arguments are eval-ed, so you may compute the name of the file to
+ * be included. The included file must not contain any incomplete
+ * function blocks. Function blocks are template text beginning with
+ * any of the macro functions @samp{CASE}, @samp{DEFINE}, @samp{FOR},
+ * @samp{IF} and @samp{WHILE}; extending through their respective
+ * terminating macro functions.
+=*/
+macro_t *
+mFunc_Include(templ_t * tpl, macro_t * mac)
+{
+ bool allocated_name;
+ char const * fname = eval_mac_expr(&allocated_name);
+
+ include_depth++;
+ if (*fname != NUL) {
+ templ_t * new_tpl = tpl_load(fname, tpl->td_file);
+ macro_t * last_mac = new_tpl->td_macros + (new_tpl->td_mac_ct - 1);
+
+ if (last_mac->md_code == FTYP_TEXT) {
+ /*
+ * Strip off trailing white space from included templates
+ */
+ char * pz = new_tpl->td_text + last_mac->md_txt_off;
+ char * pe = SPN_WHITESPACE_BACK(pz, pz);
+
+ /*
+ * IF there is no text left, remove the macro entirely
+ */
+ if (pe > pz) {
+ *pe = NUL;
+ } else {
+ new_tpl->td_mac_ct--;
+ }
+ }
+
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE) {
+ fprintf(trace_fp, TRACE_FN_INC_TPL, new_tpl->td_file);
+ if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
+ fprintf(trace_fp, TRACE_FN_INC_LINE, current_tpl->td_file,
+ mac->md_line);
+ }
+
+ gen_new_block(new_tpl);
+ tpl_unload(new_tpl);
+ current_tpl = tpl;
+ }
+ include_depth--;
+
+ if (allocated_name)
+ AGFREE((void*)fname);
+
+ return mac + 1;
+}
+
+/*=macfunc UNKNOWN
+ *
+ * what: Either a user macro or a value name.
+ * handler_proc:
+ * load_proc:
+ * unnamed:
+ *
+ * desc:
+ *
+ * The macro text has started with a name not known to AutoGen. If, at run
+ * time, it turns out to be the name of a defined macro, then that macro is
+ * invoked. If it is not, then it is a conditional expression that is
+ * evaluated only if the name is defined at the time the macro is invoked.
+ *
+ * You may not specify @code{UNKNOWN} explicitly.
+=*/
+macro_t *
+mFunc_Unknown(templ_t * pT, macro_t * pMac)
+{
+ templ_t * pInv = find_tpl(pT->td_text + pMac->md_name_off);
+ if (pInv != NULL) {
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING)
+ fprintf(trace_fp, TRACE_FN_REMAPPED, TRACE_FN_REMAP_INVOKE,
+ pMac->md_code, pT->td_file, pMac->md_line);
+ pMac->md_code = FTYP_DEFINE;
+ pMac->md_pvt = (void*)pInv;
+ parse_mac_args(pT, pMac);
+ return mFunc_Define(pT, pMac);
+ }
+
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) {
+ fprintf(trace_fp, TRACE_FN_REMAPPED, TRACE_FN_REMAP_EXPR,
+ pMac->md_code, pT->td_file, pMac->md_line);
+ fprintf(trace_fp, TRACE_FN_REMAP_BASE,
+ pT->td_text + pMac->md_name_off);
+ }
+
+ pMac->md_code = FTYP_EXPR;
+ if (pMac->md_txt_off == 0) {
+ pMac->md_res = EMIT_VALUE;
+
+ } else {
+ char* pzExpr = pT->td_text + pMac->md_txt_off;
+ switch (*pzExpr) {
+ case ';':
+ case '(':
+ pMac->md_res = EMIT_EXPRESSION;
+ break;
+
+ case '`':
+ pMac->md_res = EMIT_SHELL;
+ span_quote(pzExpr);
+ break;
+
+ case '"':
+ case '\'':
+ span_quote(pzExpr);
+ /* FALLTHROUGH */
+
+ default:
+ pMac->md_res = EMIT_STRING;
+ }
+
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING)
+ fprintf(trace_fp, TRACE_UNKNOWN_FMT, pMac->md_res, pzExpr);
+ }
+
+ return mFunc_Expr(pT, pMac);
+}
+
+
+/*=macfunc BOGUS
+ *
+ * what: Out-of-context or unknown functions are bogus.
+ * handler_proc:
+ * load_proc:
+ * unnamed:
+=*/
+macro_t*
+mFunc_Bogus(templ_t* pT, macro_t* pMac)
+{
+ char * pz = aprf(FN_BOGUS_FMT, pMac->md_code,
+ (pMac->md_code < FUNC_CT)
+ ? ag_fun_names[ pMac->md_code ]
+ : FN_BOGUS_HUH);
+
+ AG_ABEND_IN(pT, pMac, pz);
+ /* NOTREACHED */
+ return pMac;
+}
+
+
+/*=macfunc TEXT
+ *
+ * what: A block of text to be emitted.
+ * handler_proc:
+ * unnamed:
+=*/
+macro_t*
+mFunc_Text(templ_t* pT, macro_t* pMac)
+{
+ fputs(pT->td_text + pMac->md_txt_off, cur_fpstack->stk_fp);
+ fflush(cur_fpstack->stk_fp);
+ return pMac + 1;
+}
+
+
+/*=macfunc COMMENT
+ *
+ * what: A block of comment to be ignored
+ * load_proc:
+ * alias: "#"
+ *
+ * desc:
+ * This function can be specified by the user, but there will
+ * never be a situation where it will be invoked at emit time.
+ * The macro is actually removed from the internal representation.
+ *
+ * If the native macro name code is @code{#}, then the
+ * entire macro function is treated as a comment and ignored.
+ *
+ * @example
+ * [+ # say what you want, but no '+' before any ']' chars +]
+ * @end example
+=*/
+macro_t *
+mLoad_Comment(templ_t * tpl, macro_t * mac, char const ** p_scan)
+{
+ (void)tpl;
+ (void)p_scan;
+ memset((void*)mac, 0, sizeof(*mac));
+ return mac;
+}
+
+/**
+ * The default (unknown) load function.
+ *
+ * Move any text into the text offset field. This macro will change to
+ * either INVOKE or an expression function, depending on whether or not a
+ * DEFINE macro corresponds to the name. This is determined at instantiation
+ * time. This is used as the default load mechanism.
+ *
+ * @param tpl template being loaded
+ * @param mac the macro descriptor
+ * @param p_scan the input text scanning pointer
+ *
+ * @returns the macro table entry after mac
+ */
+macro_t *
+mLoad_Unknown(templ_t * tpl, macro_t * mac, char const ** unused)
+{
+ char const * scan;
+ ssize_t src_len = (size_t)mac->md_res; /* macro len */
+ (void)unused;
+
+ if (src_len <= 0)
+ goto return_emtpy_expr;
+
+ scan = (char const*)mac->md_txt_off; /* macro text */
+
+ switch (*scan) {
+ case ';':
+ {
+ char const * start = scan;
+
+ /*
+ * Strip off scheme comments
+ */
+ do {
+ scan = strchr(scan, NL);
+ if (scan == NULL)
+ goto return_emtpy_expr;
+ scan = SPN_WHITESPACE_CHARS(scan);
+ if (*scan == NUL)
+ goto return_emtpy_expr;
+ } while (*scan == ';');
+ src_len -= scan - start;
+ break;
+ }
+
+ case '[':
+ case '.':
+ {
+ /*
+ * We are going to recopy the definition name, this time as a
+ * canonical name (i.e. including '[', ']' and '.' characters,
+ * but with all blanks squeezed out)
+ */
+ char * cname = tpl->td_text + mac->md_name_off;
+ size_t cname_len = strlen(cname);
+
+ /*
+ * Move back the source pointer. We may have skipped blanks,
+ * so skip over however many first, then back up over the name.
+ * We have found a name, so we won't back up past the start.
+ */
+ while (IS_WHITESPACE_CHAR(scan[-1])) scan--, src_len++;
+ scan -= cname_len;
+ src_len += cname_len;
+
+ /*
+ * Now copy over the full canonical name. Check for errors.
+ * Advance the scan pointer to just past the name we've copied.
+ */
+ {
+ size_t rem_len = canonical_name(cname, scan, (int)src_len);
+ if (rem_len > src_len)
+ AG_ABEND_IN(tpl, mac, LD_UNKNOWN_INVAL_DEF);
+
+ scan += src_len - rem_len;
+ src_len = rem_len;
+ }
+
+ /*
+ * Where we are stashing text ("td_scan") gets set to just past the
+ * NUL byte terminating the name. "cname" is now longer than before.
+ */
+ tpl->td_scan = cname + strlen(cname) + 1;
+ if (src_len <= 0)
+ goto return_emtpy_expr;
+ break;
+ }
+ }
+
+ /*
+ * Copy the expression (the remaining text)
+ */
+ {
+ char * dest = tpl->td_scan; /* next text dest */
+ mac->md_txt_off = (dest - tpl->td_text);
+ mac->md_res = 0;
+ memcpy(dest, scan, src_len);
+ dest += src_len;
+ *(dest++) = NUL;
+ *dest = NUL; /* double terminate */
+ tpl->td_scan = dest;
+ }
+
+ return mac + 1;
+
+ return_emtpy_expr:
+ mac->md_txt_off = 0;
+ mac->md_res = 0;
+ return mac + 1;
+}
+
+
+/**
+ * Some functions are known to AutoGen, but invalid out of context.
+ * For example, ELIF, ELSE and ENDIF are all known to AutoGen.
+ * However, the load function pointer for those functions points
+ * here, until an "IF" function is encountered.
+ *
+ * @param tpl template being loaded
+ * @param mac the macro descriptor
+ * @param p_scan the input text scanning pointer
+ *
+ * @returns the macro table entry after mac
+ */
+macro_t *
+mLoad_Bogus(templ_t * tpl, macro_t * mac, char const ** p_scan)
+{
+ char const * pzSrc = (char const*)mac->md_txt_off; /* macro text */
+ char const * pzMac;
+
+ char z[ 64 ];
+ (void)p_scan;
+
+ if (pzSrc != NULL) {
+ z[0] = ':';
+ z[1] = z[2] = ' ';
+ strncpy(z+3, pzSrc, (size_t)60);
+ z[63] = NUL;
+ pzSrc = z;
+ }
+ else
+ pzSrc = zNil;
+
+ {
+ int ix = mac->md_code;
+ if ((unsigned)ix >= FUNC_CT)
+ ix = 0;
+
+ pzMac = ag_fun_names[ ix ];
+ }
+
+ pzSrc = aprf(LD_BOGUS_UNKNOWN, tpl->td_file, mac->md_line, pzMac, pzSrc);
+
+ AG_ABEND_IN(tpl, mac, pzSrc);
+ /* NOTREACHED */
+ return NULL;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/functions.c */
diff --git a/agen5/functions.h b/agen5/functions.h
new file mode 100644
index 0000000..b19b0a9
--- /dev/null
+++ b/agen5/functions.h
@@ -0,0 +1,325 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (functions.h)
+ *
+ * It has been AutoGen-ed
+ * From the definitions functions.def
+ * and the template file functions.tpl
+ *
+ * Tables of Text Functions for AutoGen
+ *
+ * copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The 24 AutoGen macros are tagged with special attributes:
+ *
+ * A - is invoked via an alias
+ *
+ * U - is unnamed. May *not* be explicitly invoked. May not have
+ * have an alias. These three are used by AutoGen for its purposes.
+ *
+ * L - has a special load procedure defined
+ *
+ * C - in context. May be explicitly invoked in certain situations.
+ * For example, "ELSE" may only be specified within an "IF" block.
+ * Their load procedures are enabled by the block macro (e.g. IF),
+ * and disabled by the block ending macro (e.g. ENDIF).
+ * While disabled, the load procedure is the "Bogus" method.
+ *
+ * If a function is neither has a special load procedure nor is
+ * situational, then the "Unknown" load method is applied.
+ *
+ * R - has a special remove (unload) procedure
+ *
+ * H - has a handler procedure defined. Only these procedures should
+ * be encountered by the dispatcher during processing.
+ *
+ * U L H - BOGUS Out-of-context or unknown functions are bogus.
+ * L H - BREAK Leave a FOR or WHILE macro
+ * L H - CASE Select one of several template blocks
+ * A L - COMMENT A block of comment to be ignored
+ * L H - CONTINUE Skip to end of a FOR or WHILE macro.
+ * L H - DEBUG Print debug message to trace output
+ * L RH - DEFINE Define a user AutoGen macro
+ * C - ELIF Alternate Conditional Template Block
+ * C - ELSE Alternate Template Block
+ * C - ENDDEF Ends a macro definition.
+ * C - ENDFOR Terminates the @code{FOR} function template block
+ * C - ENDIF Terminate the @code{IF} Template Block
+ * C - ENDWHILE Terminate the @code{WHILE} Template Block
+ * C - ESAC Terminate the @code{CASE} Template Block
+ * A L H - EXPR Evaluate and emit an Expression
+ * L H - FOR Emit a template block multiple times
+ * L H - IF Conditionally Emit a Template Block
+ * L H - INCLUDE Read in and emit a template block
+ * H - INVOKE Invoke a User Defined Macro
+ * L H - RETURN Leave an INVOKE-d (DEFINE) macro
+ * A C R - SELECT Selection block for CASE function
+ * U H - TEXT A block of text to be emitted.
+ * U L H - UNKNOWN Either a user macro or a value name.
+ * L H - WHILE Conditionally loop over a Template Block
+ */
+#ifndef AUTOGEN_FUNCTIONS_H_GUARD
+#define AUTOGEN_FUNCTIONS_H_GUARD 1
+
+#define FUNC_CT 24
+
+/**
+ * Enumerate all the AutoGen macro types.
+ */
+typedef enum {
+ FTYP_BOGUS, /* Out-of-context or unknown functions are bogus. */
+ FTYP_BREAK, /* Leave a FOR or WHILE macro */
+ FTYP_CASE, /* Select one of several template blocks */
+ FTYP_COMMENT, /* A block of comment to be ignored */
+ FTYP_CONTINUE, /* Skip to end of a FOR or WHILE macro. */
+ FTYP_DEBUG, /* Print debug message to trace output */
+ FTYP_DEFINE, /* Define a user AutoGen macro */
+ FTYP_ELIF, /* Alternate Conditional Template Block */
+ FTYP_ELSE, /* Alternate Template Block */
+ FTYP_ENDDEF, /* Ends a macro definition. */
+ FTYP_ENDFOR, /* Terminates the @code{FOR} function template block */
+ FTYP_ENDIF, /* Terminate the @code{IF} Template Block */
+ FTYP_ENDWHILE, /* Terminate the @code{WHILE} Template Block */
+ FTYP_ESAC, /* Terminate the @code{CASE} Template Block */
+ FTYP_EXPR, /* Evaluate and emit an Expression */
+ FTYP_FOR, /* Emit a template block multiple times */
+ FTYP_IF, /* Conditionally Emit a Template Block */
+ FTYP_INCLUDE, /* Read in and emit a template block */
+ FTYP_INVOKE, /* Invoke a User Defined Macro */
+ FTYP_RETURN, /* Leave an INVOKE-d (DEFINE) macro */
+ FTYP_SELECT, /* Selection block for CASE function */
+ FTYP_TEXT, /* A block of text to be emitted. */
+ FTYP_UNKNOWN, /* Either a user macro or a value name. */
+ FTYP_WHILE, /* Conditionally loop over a Template Block */
+
+ FTYP_SELECT_COMPARE_FULL = 0x8000, /* *==* */
+ FTYP_SELECT_COMPARE_SKP_START = 0x8001, /* *== */
+ FTYP_SELECT_COMPARE_SKP_END = 0x8002, /* ==* */
+ FTYP_SELECT_COMPARE = 0x8003, /* == */
+
+ FTYP_SELECT_EQUIVALENT_FULL = 0x8004, /* *=* */
+ FTYP_SELECT_EQUIVALENT_SKP_START = 0x8005, /* *= */
+ FTYP_SELECT_EQUIVALENT_SKP_END = 0x8006, /* =* */
+ FTYP_SELECT_EQUIVALENT = 0x8007, /* = */
+
+ FTYP_SELECT_MATCH_FULL = 0x8008, /* *~~* */
+ FTYP_SELECT_MATCH_SKP_START = 0x8009, /* *~~ */
+ FTYP_SELECT_MATCH_SKP_END = 0x800A, /* ~~* */
+ FTYP_SELECT_MATCH = 0x800B, /* ~~ */
+
+ FTYP_SELECT_EQV_MATCH_FULL = 0x800C, /* *~* */
+ FTYP_SELECT_EQV_MATCH_SKP_START = 0x800D, /* *~ */
+ FTYP_SELECT_EQV_MATCH_SKP_END = 0x800E, /* ~* */
+ FTYP_SELECT_EQV_MATCH = 0x800F, /* ~ */
+
+ FTYP_SELECT_MATCH_ANYTHING = 0x801C, /* * */
+ FTYP_SELECT_MATCH_EXISTENCE = 0x801D, /* +E */
+ FTYP_SELECT_MATCH_NONEXISTENCE = 0x801E /* !E */
+} mac_func_t;
+
+/**
+ * The function processing procedures.
+ */
+hdlr_proc_t
+ mFunc_Bogus, mFunc_Break, mFunc_Case, mFunc_Debug, mFunc_Define,
+ mFunc_Expr, mFunc_For, mFunc_If, mFunc_Include, mFunc_Invoke,
+ mFunc_Return, mFunc_Text, mFunc_Unknown, mFunc_While;
+
+/**
+ * Template Loading Functions.
+ */
+load_proc_t
+ mLoad_Bogus, mLoad_Case, mLoad_Comment, mLoad_Debug, mLoad_Define,
+ mLoad_Ending, mLoad_Expr, mLoad_For, mLoad_If, mLoad_Leave,
+ mLoad_Unknown, mLoad_While;
+
+/* tpParse.c use only * * * * * * * * * * * * * * * */
+/**
+ * Parsing function tables for load processing (template scanning phase).
+ */
+static load_proc_p_t const base_load_table[ FUNC_CT ] = {
+ /* BOGUS */ mLoad_Bogus,
+ /* BREAK */ mLoad_Leave,
+ /* CASE */ mLoad_Case,
+ /* COMMENT */ mLoad_Comment,
+ /* CONTINUE */ mLoad_Leave,
+ /* DEBUG */ mLoad_Debug,
+ /* DEFINE */ mLoad_Define,
+ /* ELIF */ mLoad_Bogus /*dynamic*/,
+ /* ELSE */ mLoad_Bogus /*dynamic*/,
+ /* ENDDEF */ mLoad_Bogus /*dynamic*/,
+ /* ENDFOR */ mLoad_Bogus /*dynamic*/,
+ /* ENDIF */ mLoad_Bogus /*dynamic*/,
+ /* ENDWHILE */ mLoad_Bogus /*dynamic*/,
+ /* ESAC */ mLoad_Bogus /*dynamic*/,
+ /* EXPR */ mLoad_Expr,
+ /* FOR */ mLoad_For,
+ /* IF */ mLoad_If,
+ /* INCLUDE */ mLoad_Expr,
+ /* INVOKE */ mLoad_Unknown /*default*/,
+ /* RETURN */ mLoad_Leave,
+ /* SELECT */ mLoad_Bogus /*dynamic*/,
+ /* TEXT */ mLoad_Unknown /*default*/,
+ /* UNKNOWN */ mLoad_Unknown,
+ /* WHILE */ mLoad_While
+};
+
+/**
+ * This global pointer is used to switch parsing tables.
+ * The block functions (CASE, DEFINE, FOR, and IF) change this to point
+ * to their tables that include relevant additional functions.
+ */
+load_proc_p_t const * load_proc_table = base_load_table;
+
+/**
+ * name-to-function type mapping table.
+ * This table must be sorted alphabetically by the content
+ * of the naming string.
+ */
+typedef struct fn_name_type fn_name_type_t;
+struct fn_name_type {
+ size_t cmpLen; /*!< compare length (sans NUL) */
+ char const * pName; /*!< ptr to name */
+ mac_func_t fType; /*!< function type enum */
+};
+
+/**
+ * Define all the strings that are used to determine the function enumeration
+ * number. These are used in a table separated by aliases and sorted by these
+ * ASCII values.
+ */
+static char const zFnStrg[142] =
+ "BREAK\0" "CASE\0" "#\0" "CONTINUE\0" "DEBUG\0"
+ "DEFINE\0" "ELIF\0" "ELSE\0" "ENDDEF\0" "ENDFOR\0"
+ "ENDIF\0" "ENDWHILE\0" "ESAC\0" "-\0" "?\0"
+ "%\0" ";\0" "(\0" "`\0" "\"\0"
+ "'\0" ".\0" "FOR\0" "IF\0" "INCLUDE\0"
+ "INVOKE\0" "RETURN\0" "~\0" "=\0" "*\0"
+ "!\0" "+\0" "WHILE\0";
+
+/**
+ * The number of names by which the macros go.
+ * Some have multiple names (aliases, e.g. selection clauses).
+ */
+#define FUNC_ALIAS_LOW_INDEX 0
+#define FUNC_ALIAS_HIGH_INDEX 14
+#define FUNC_NAMES_LOW_INDEX 15
+#define FUNC_NAMES_HIGH_INDEX 32
+#define FUNCTION_NAME_CT 33
+
+/* * * * * * * * tpParse.c use only * * * * * * * * * * * * * * */
+/**
+ * And now, the table separated by aliasing and then sorted by string content
+ */
+static fn_name_type_t const fn_name_types[ FUNCTION_NAME_CT ] = {
+ { 1, zFnStrg +132, FTYP_SELECT },
+ { 1, zFnStrg + 91, FTYP_EXPR },
+ { 1, zFnStrg + 11, FTYP_COMMENT },
+ { 1, zFnStrg + 83, FTYP_EXPR },
+ { 1, zFnStrg + 93, FTYP_EXPR },
+ { 1, zFnStrg + 87, FTYP_EXPR },
+ { 1, zFnStrg +130, FTYP_SELECT },
+ { 1, zFnStrg +134, FTYP_SELECT },
+ { 1, zFnStrg + 79, FTYP_EXPR },
+ { 1, zFnStrg + 95, FTYP_EXPR },
+ { 1, zFnStrg + 85, FTYP_EXPR },
+ { 1, zFnStrg +128, FTYP_SELECT },
+ { 1, zFnStrg + 81, FTYP_EXPR },
+ { 1, zFnStrg + 89, FTYP_EXPR },
+ { 1, zFnStrg +126, FTYP_SELECT },
+
+ { 5, zFnStrg + 0, FTYP_BREAK },
+ { 4, zFnStrg + 6, FTYP_CASE },
+ { 8, zFnStrg + 13, FTYP_CONTINUE },
+ { 5, zFnStrg + 22, FTYP_DEBUG },
+ { 6, zFnStrg + 28, FTYP_DEFINE },
+ { 4, zFnStrg + 35, FTYP_ELIF },
+ { 4, zFnStrg + 40, FTYP_ELSE },
+ { 6, zFnStrg + 45, FTYP_ENDDEF },
+ { 6, zFnStrg + 52, FTYP_ENDFOR },
+ { 5, zFnStrg + 59, FTYP_ENDIF },
+ { 8, zFnStrg + 65, FTYP_ENDWHILE },
+ { 4, zFnStrg + 74, FTYP_ESAC },
+ { 3, zFnStrg + 97, FTYP_FOR },
+ { 2, zFnStrg +101, FTYP_IF },
+ { 7, zFnStrg +104, FTYP_INCLUDE },
+ { 6, zFnStrg +112, FTYP_INVOKE },
+ { 6, zFnStrg +119, FTYP_RETURN },
+ { 5, zFnStrg +136, FTYP_WHILE } };
+
+static char const * const ag_fun_names[ FUNC_CT ] = {
+ "Bogus", zFnStrg+0, zFnStrg+6, "COMMENT", zFnStrg+13,
+ zFnStrg+22, zFnStrg+28, zFnStrg+35, zFnStrg+40, zFnStrg+45,
+ zFnStrg+52, zFnStrg+59, zFnStrg+65, zFnStrg+74, "EXPR",
+ zFnStrg+97, zFnStrg+101, zFnStrg+104, zFnStrg+112, zFnStrg+119,
+ "SELECT", "Text", "Unknown", zFnStrg+136 };
+
+/* * * * * * * * tpProcess.c use only * * * * * * * * * * * * * */
+/**
+ * Template Processing Function Table
+ *
+ * Pointers to the procedure to call when the function code
+ * is encountered.
+ */
+static hdlr_proc_p_t const load_procs[ FUNC_CT ] = {
+ /* BOGUS */ mFunc_Bogus,
+ /* BREAK */ mFunc_Break,
+ /* CASE */ mFunc_Case,
+ /* COMMENT */ mFunc_Bogus,
+ /* CONTINUE */ mFunc_Break,
+ /* DEBUG */ mFunc_Debug,
+ /* DEFINE */ mFunc_Define,
+ /* ELIF */ mFunc_Bogus,
+ /* ELSE */ mFunc_Bogus,
+ /* ENDDEF */ mFunc_Bogus,
+ /* ENDFOR */ mFunc_Bogus,
+ /* ENDIF */ mFunc_Bogus,
+ /* ENDWHILE */ mFunc_Bogus,
+ /* ESAC */ mFunc_Bogus,
+ /* EXPR */ mFunc_Expr,
+ /* FOR */ mFunc_For,
+ /* IF */ mFunc_If,
+ /* INCLUDE */ mFunc_Include,
+ /* INVOKE */ mFunc_Invoke,
+ /* RETURN */ mFunc_Return,
+ /* SELECT */ mFunc_Bogus,
+ /* TEXT */ mFunc_Text,
+ /* UNKNOWN */ mFunc_Unknown,
+ /* WHILE */ mFunc_While
+};
+
+/* * * * * * * * * * tpLoad.c use only * * * * * * * * * * * * * */
+/**
+ * Template Unloading Function Table
+ *
+ * Pointers to the procedure to call when the function code
+ * is encountered in a template being unloaded.
+ */
+unload_proc_t mUnload_Define, mUnload_Select;
+
+static unload_proc_p_t const unload_procs[ FUNC_CT ] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, mUnload_Define, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ mUnload_Select, NULL, NULL, NULL
+};
+
+#define FUNCTION_CKSUM ((unsigned short)0x22FE)
+
+#endif /* AUTOGEN_FUNCTIONS_H_GUARD */
+/* functions.h ends here */
diff --git a/agen5/guile-iface.def b/agen5/guile-iface.def
new file mode 100644
index 0000000..f5d6359
--- /dev/null
+++ b/agen5/guile-iface.def
@@ -0,0 +1,183 @@
+
+AutoGen Definitions guile-iface.tpl;
+
+#if 0
+iface = {
+ i-name = ;
+ i-args = ;
+ i-impl = { i-end = ''; i-code = ''; };
+};
+#endif
+
+invalid = '200000-200003',
+ '-106000';
+
+iface = {
+ i-name = bool_p;
+ i-args = '_b';
+ i-impl = { i-end = '107'; i-code = 'SCM_BOOLP(_b)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_bool(_b)'; };
+};
+
+iface = {
+ i-name = char;
+ i-args = '_c';
+ i-impl = { i-end = '107'; i-code = 'gh_scm2char(_c)'; };
+ i-impl = { i-end = '201'; i-code = 'SCM_CHAR(_c)'; };
+};
+
+iface = {
+ i-name = chars;
+ i-args = '_s';
+ i-impl = { i-end = '107'; i-code = 'SCM_CHARS(_s)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_i_string_chars(_s)'; };
+};
+
+iface = {
+ i-name = falsep;
+ i-args = '_r';
+ i-impl = { i-end = '107'; i-code = 'SCM_FALSEP(_r)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_false(_r)'; };
+};
+
+iface = {
+ i-name = from_long;
+ i-args = '_l';
+ i-impl = { i-end = '107'; i-code = 'gh_long2scm(_l)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_from_long(_l)'; };
+};
+
+iface = {
+ i-name = int2scm;
+ i-args = '_i';
+ i-impl = { i-end = '107'; i-code = 'gh_int2scm(_i)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_from_int(_i)'; };
+};
+
+iface = {
+ i-name = is_proc;
+ i-args = '_p';
+ i-impl = { i-end = '107'; i-code = 'SCM_NFALSEP( scm_procedure_p(_p))'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_true( scm_procedure_p(_p))'; };
+};
+
+iface = {
+ i-name = list_p;
+ i-args = '_l';
+ i-impl = { i-end = '107'; i-code = 'SCM_NFALSEP( scm_list_p(_l))'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_true( scm_list_p(_l))'; };
+};
+
+iface = {
+ i-name = listofnull;
+ i-impl = { i-end = '107'; i-code = 'scm_listofnull'; };
+ i-impl = { i-end = '201'; i-code = 'scm_list_1(SCM_EOL)'; };
+};
+
+iface = {
+ i-name = long2scm;
+ i-args = '_i';
+ i-impl = { i-end = '107'; i-code = 'gh_long2scm(_i)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_from_long(_i)'; };
+};
+
+iface = {
+ i-name = nfalsep;
+ i-args = '_r';
+ i-impl = { i-end = '107'; i-code = 'SCM_NFALSEP(_r)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_true(_r)'; };
+};
+
+iface = {
+ i-name = nullp;
+ i-args = '_m';
+ i-impl = { i-end = '107'; i-code = 'SCM_NULLP(_m)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_null(_m)'; };
+};
+
+iface = {
+ i-name = num_p;
+ i-args = '_n';
+ i-impl = { i-end = '107'; i-code = 'SCM_NUMBERP(_n)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_number(_n)'; };
+};
+
+iface = {
+ i-name = pair_p;
+ i-args = '_p';
+ i-impl = { i-end = '107'; i-code = 'SCM_NFALSEP( scm_pair_p(_p))'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_true( scm_pair_p(_p))'; };
+};
+
+iface = {
+ i-name = str02scm;
+ i-args = '_s';
+ i-impl = { i-end = '107'; i-code = 'scm_makfrom0str(_s)'; };
+ i-impl = { i-end = '200'; i-code = 'scm_from_locale_string(_s)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_from_utf8_string(_s)'; };
+};
+
+iface = {
+ i-name = str2scm;
+ i-args = '_st,_sz';
+ i-impl = { i-end = '107'; i-code = 'scm_mem2string(_st,_sz)'; };
+ i-impl = { i-end = '200'; i-code = 'scm_from_locale_stringn(_st,_sz)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_from_utf8_stringn(_st,_sz)'; };
+};
+
+iface = {
+ i-name = to_newstr;
+ i-args = '_s';
+ i-impl = { i-end = '107'; i-code = 'gh_scm2newstr(_s, NULL)'; };
+ i-impl = { i-end = '200'; i-code = 'scm_to_locale_string(_s)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_to_utf8_string(_s)'; };
+};
+
+iface = {
+ i-name = string_p;
+ i-args = '_s';
+ i-impl = { i-end = '107'; i-code = 'SCM_STRINGP(_s)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_string(_s)'; };
+};
+
+iface = {
+ i-name = strlen;
+ i-args = '_s';
+ i-impl = { i-end = '107'; i-code = 'SCM_STRING_LENGTH(_s)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_c_string_length(_s)'; };
+};
+
+iface = {
+ i-name = sym_p;
+ i-args = '_s';
+ i-impl = { i-end = '107'; i-code = 'SCM_SYMBOLP(_s)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_symbol(_s)'; };
+};
+
+iface = {
+ i-name = to_int;
+ i-args = '_i';
+ i-impl = { i-end = '107'; i-code = 'gh_scm2int(_i)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_to_int(_i)'; };
+};
+
+iface = {
+ i-name = to_long;
+ i-args = '_v';
+ i-impl = { i-end = '107'; i-code = 'gh_scm2long(_v)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_to_long(_v)'; };
+};
+
+iface = {
+ i-name = to_ulong;
+ i-args = '_v';
+ i-impl = { i-end = '107'; i-code = 'gh_scm2ulong(_v)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_to_ulong(_v)'; };
+};
+
+iface = {
+ i-name = vec_p;
+ i-args = '_v';
+ i-impl = { i-end = '107'; i-code = 'SCM_VECTORP(_v)'; };
+ i-impl = { i-end = '201'; i-code = 'scm_is_vector(_v)'; };
+};
diff --git a/agen5/guile-iface.h b/agen5/guile-iface.h
new file mode 100644
index 0000000..f26122d
--- /dev/null
+++ b/agen5/guile-iface.h
@@ -0,0 +1,95 @@
+#ifndef MUTATING_GUILE_IFACE_H_GUARD
+#define MUTATING_GUILE_IFACE_H_GUARD 1
+
+#if (GUILE_VERSION >= 200000) && (GUILE_VERSION <= 200003)
+# error AutoGen does not work with this version of Guile
+ choke me.
+
+#elif (GUILE_VERSION <= 106000)
+# error AutoGen does not work with this version of Guile
+ choke me.
+
+#elif GUILE_VERSION < 107000
+# define AG_SCM_BOOL_P(_b) SCM_BOOLP(_b)
+# define AG_SCM_CHAR(_c) gh_scm2char(_c)
+# define AG_SCM_CHARS(_s) SCM_CHARS(_s)
+# define AG_SCM_FALSEP(_r) SCM_FALSEP(_r)
+# define AG_SCM_FROM_LONG(_l) gh_long2scm(_l)
+# define AG_SCM_INT2SCM(_i) gh_int2scm(_i)
+# define AG_SCM_IS_PROC(_p) SCM_NFALSEP( scm_procedure_p(_p))
+# define AG_SCM_LIST_P(_l) SCM_NFALSEP( scm_list_p(_l))
+# define AG_SCM_LISTOFNULL() scm_listofnull
+# define AG_SCM_LONG2SCM(_i) gh_long2scm(_i)
+# define AG_SCM_NFALSEP(_r) SCM_NFALSEP(_r)
+# define AG_SCM_NULLP(_m) SCM_NULLP(_m)
+# define AG_SCM_NUM_P(_n) SCM_NUMBERP(_n)
+# define AG_SCM_PAIR_P(_p) SCM_NFALSEP( scm_pair_p(_p))
+# define AG_SCM_STR02SCM(_s) scm_makfrom0str(_s)
+# define AG_SCM_STR2SCM(_st,_sz) scm_mem2string(_st,_sz)
+# define AG_SCM_TO_NEWSTR(_s) gh_scm2newstr(_s, NULL)
+# define AG_SCM_STRING_P(_s) SCM_STRINGP(_s)
+# define AG_SCM_STRLEN(_s) SCM_STRING_LENGTH(_s)
+# define AG_SCM_SYM_P(_s) SCM_SYMBOLP(_s)
+# define AG_SCM_TO_INT(_i) gh_scm2int(_i)
+# define AG_SCM_TO_LONG(_v) gh_scm2long(_v)
+# define AG_SCM_TO_ULONG(_v) gh_scm2ulong(_v)
+# define AG_SCM_VEC_P(_v) SCM_VECTORP(_v)
+
+#elif GUILE_VERSION < 200000
+# define AG_SCM_BOOL_P(_b) scm_is_bool(_b)
+# define AG_SCM_CHAR(_c) SCM_CHAR(_c)
+# define AG_SCM_CHARS(_s) scm_i_string_chars(_s)
+# define AG_SCM_FALSEP(_r) scm_is_false(_r)
+# define AG_SCM_FROM_LONG(_l) scm_from_long(_l)
+# define AG_SCM_INT2SCM(_i) scm_from_int(_i)
+# define AG_SCM_IS_PROC(_p) scm_is_true( scm_procedure_p(_p))
+# define AG_SCM_LIST_P(_l) scm_is_true( scm_list_p(_l))
+# define AG_SCM_LISTOFNULL() scm_list_1(SCM_EOL)
+# define AG_SCM_LONG2SCM(_i) scm_from_long(_i)
+# define AG_SCM_NFALSEP(_r) scm_is_true(_r)
+# define AG_SCM_NULLP(_m) scm_is_null(_m)
+# define AG_SCM_NUM_P(_n) scm_is_number(_n)
+# define AG_SCM_PAIR_P(_p) scm_is_true( scm_pair_p(_p))
+# define AG_SCM_STR02SCM(_s) scm_from_locale_string(_s)
+# define AG_SCM_STR2SCM(_st,_sz) scm_from_locale_stringn(_st,_sz)
+# define AG_SCM_TO_NEWSTR(_s) scm_to_locale_string(_s)
+# define AG_SCM_STRING_P(_s) scm_is_string(_s)
+# define AG_SCM_STRLEN(_s) scm_c_string_length(_s)
+# define AG_SCM_SYM_P(_s) scm_is_symbol(_s)
+# define AG_SCM_TO_INT(_i) scm_to_int(_i)
+# define AG_SCM_TO_LONG(_v) scm_to_long(_v)
+# define AG_SCM_TO_ULONG(_v) scm_to_ulong(_v)
+# define AG_SCM_VEC_P(_v) scm_is_vector(_v)
+
+#elif GUILE_VERSION < 201000
+# define AG_SCM_BOOL_P(_b) scm_is_bool(_b)
+# define AG_SCM_CHAR(_c) SCM_CHAR(_c)
+# define AG_SCM_CHARS(_s) scm_i_string_chars(_s)
+# define AG_SCM_FALSEP(_r) scm_is_false(_r)
+# define AG_SCM_FROM_LONG(_l) scm_from_long(_l)
+# define AG_SCM_INT2SCM(_i) scm_from_int(_i)
+# define AG_SCM_IS_PROC(_p) scm_is_true( scm_procedure_p(_p))
+# define AG_SCM_LIST_P(_l) scm_is_true( scm_list_p(_l))
+# define AG_SCM_LISTOFNULL() scm_list_1(SCM_EOL)
+# define AG_SCM_LONG2SCM(_i) scm_from_long(_i)
+# define AG_SCM_NFALSEP(_r) scm_is_true(_r)
+# define AG_SCM_NULLP(_m) scm_is_null(_m)
+# define AG_SCM_NUM_P(_n) scm_is_number(_n)
+# define AG_SCM_PAIR_P(_p) scm_is_true( scm_pair_p(_p))
+# define AG_SCM_STR02SCM(_s) scm_from_utf8_string(_s)
+# define AG_SCM_STR2SCM(_st,_sz) scm_from_utf8_stringn(_st,_sz)
+# define AG_SCM_TO_NEWSTR(_s) scm_to_utf8_string(_s)
+# define AG_SCM_STRING_P(_s) scm_is_string(_s)
+# define AG_SCM_STRLEN(_s) scm_c_string_length(_s)
+# define AG_SCM_SYM_P(_s) scm_is_symbol(_s)
+# define AG_SCM_TO_INT(_i) scm_to_int(_i)
+# define AG_SCM_TO_LONG(_v) scm_to_long(_v)
+# define AG_SCM_TO_ULONG(_v) scm_to_ulong(_v)
+# define AG_SCM_VEC_P(_v) scm_is_vector(_v)
+
+#else
+# error unknown GUILE_VERSION
+ choke me.
+#endif
+
+#endif /* MUTATING_GUILE_IFACE_H_GUARD */
diff --git a/agen5/guile-iface.tpl b/agen5/guile-iface.tpl
new file mode 100644
index 0000000..e94cb89
--- /dev/null
+++ b/agen5/guile-iface.tpl
@@ -0,0 +1,116 @@
+[= AutoGen5 Template h =]
+[=
+(emit (make-header-guard "mutating"))
+(shell "ranged='#if'
+guile_range() {
+ local lo=${1%%-*}
+ local hi=${1##*-}
+ if test ${#lo} -eq 0
+ then echo \"${ranged} (GUILE_VERSION <= $hi)\"
+ elif test ${#hi} -eq 0
+ then echo \"${ranged} (GUILE_VERSION >= $lo)\"
+ else echo \"${ranged} (GUILE_VERSION >= $lo) && (GUILE_VERSION <= $hi)\"
+ fi
+ ranged='#elif'
+}") =]
+[=
+
+FOR invalid \=]
+
+[= (shell (string-append "guile_range " (get "invalid"))) =]
+# error AutoGen does not work with this version of Guile
+ choke me.
+[=
+ENDFOR invalid =]
+[= (out-push-new) =]
+set -- $(sort -n -u <<_EOF_
+[= (join "\n" (stack "iface.i-impl.i-end")) =]
+_EOF_
+)
+v_list="$*"
+r_list=$(ix=$# ; while (( ix > 0 )) ; do eval echo \${$ix}
+ (( ix = ix - 1 )) ; done)
+i_list="[= (join " " (stack "iface.i-name")) =]"
+PS4='>${FUNC_NAME:-ag}> '
+
+fill_in() {
+ for v in $r_list
+ do
+ eval f=\"\${${name}_vals[$v]}\"
+ if test ${#f} -gt 0
+ then val="$f"
+ else eval ${name}_vals[$v]=\"$val\"
+ fi
+ done
+}
+
+emit_iface_macro() {
+ NM=$(echo $i | tr a-z- A-Z_)
+ eval NM=\"$NM\(\${${i}_args}\)\"
+ if test ${#code} -lt 40 -a ${#NM} -lt 22
+ then
+ printf '# define AG_SCM_%-21s %s\n' "${NM}" "${code}"
+ else
+ printf '# define AG_SCM_%-21s \\\n %s\n' "${NM}" "${code}"
+ fi
+}
+
+emit_iface_type() {
+ local nm=ag_scm_$(echo $i | tr A-Z- a-z_)
+ printf '# define %-28s %s\n' $nm "$code"
+}
+
+prt_tbl() {
+ if='#[= (if (exist? "invalid") "elif" "if ") =]'
+ for v in $v_list
+ do
+ printf '%s GUILE_VERSION < %s000\n' "$if" "$v"
+ if='#elif'
+
+ for i in $i_list
+ do
+ eval type=\"\${${i}_type}\"
+ eval code=\"\${${i}_vals[$v]}\"
+ case "$type" in
+ ( macro | '' ) emit_iface_macro ;;
+ ( typedef ) emit_iface_type ;;
+ esac
+ done
+
+ echo
+ done
+}
+[=
+(shell (out-pop #t)) =][=
+
+FOR iface =][=
+
+ (shell (string-append
+ "val='" (get "i-impl[].i-code") "'\n"
+ (get "i-name") "_args='" (get "i-args") "'\n"
+ "name=" (get "i-name") "\n"
+ (get "i-name") "_type=" (get "i-type" "macro")
+
+ )) =][=
+
+ FOR i-impl =][=
+
+ (shell (string-append
+ (get "i-name") "_vals[" (get "i-end") "]='" (get "i-code") "'"
+ )) =][=
+
+ ENDFOR =][=
+
+ `fill_in` =][=
+
+ENDFOR iface =][=
+
+`prt_tbl`
+=]
+
+#else
+# error unknown GUILE_VERSION
+ choke me.
+#endif
+
+#endif /* [= (. header-guard) =] */
diff --git a/agen5/invoke-autogen.texi b/agen5/invoke-autogen.texi
new file mode 100644
index 0000000..31c65f9
--- /dev/null
+++ b/agen5/invoke-autogen.texi
@@ -0,0 +1,872 @@
+@node autogen Invocation
+@chapter Invoking autogen
+@pindex autogen
+@cindex The Automated Program Generator
+@ignore
+# -*- buffer-read-only: t -*- vi: set ro:
+#
+# DO NOT EDIT THIS FILE (invoke-autogen.texi)
+#
+# It has been AutoGen-ed August 11, 2012 at 09:41:40 AM by AutoGen 5.16.2pre7
+# From the definitions /old-home/bkorb/ag/ag/agen5/opts.def
+# and the template file agtexi-cmd.tpl
+@end ignore
+
+AutoGen creates text files from templates using external definitions.
+
+@code{AutoGen} is designed for generating program files that contain
+repetitive text with varied substitutions. The goal is to simplify the
+maintenance of programs that contain large amounts of repetitious text.
+This is especially valuable if there are several blocks of such text
+that must be kept synchronized.
+
+One common example is the problem of maintaining the code required for
+processing program options. Processing options requires a minimum of
+four different constructs be kept in proper order in different places
+in your program. You need at least: The flag character in the flag
+string, code to process the flag when it is encountered, a global
+state variable or two, and a line in the usage text.
+You will need more things besides this if you choose to implement
+long option names, configuration file processing, environment variables
+and so on.
+
+All of this can be done mechanically; with the proper templates
+and this program.
+
+
+This chapter was generated by @strong{AutoGen},
+using the @code{agtexi-cmd} template and the option descriptions for the @code{autogen} program.
+This software is released under the GNU General Public License, version 3 or later.
+
+@menu
+* autogen usage:: autogen help/usage (@option{--help})
+* autogen input-select:: input-select options
+* autogen out-handling:: out-handling options
+* autogen debug-tpl:: debug-tpl options
+* autogen processing:: processing options
+* autogen dep-track:: dep-track options
+* autogen config:: presetting/configuring autogen
+* autogen exit status:: exit status
+* autogen Examples:: Examples
+@end menu
+
+@node autogen usage
+@section autogen help/usage (@option{--help})
+@cindex autogen help
+
+This is the automatically generated usage text for autogen.
+
+The text printed is the same whether selected with the @code{help} option
+(@option{--help}) or the @code{more-help} option (@option{--more-help}). @code{more-help} will print
+the usage text by passing it through a pager program.
+@code{more-help} is disabled on platforms without a working
+@code{fork(2)} function. The @code{PAGER} environment variable is
+used to select the program, defaulting to @file{more}. Both will exit
+with a status code of 0.
+
+@exampleindent 0
+@example
+autogen (GNU AutoGen) - The Automated Program Generator - Ver. 5.16.2pre7
+USAGE: autogen [ -<flag> [<val>] | --<name>[@{=| @}<val>] ]... [ <def-file> ]
+
+The following options select definitions, templates and scheme functions
+to use:
+
+ Flg Arg Option-Name Description
+ -L Str templ-dirs Template search directory list
+ - may appear multiple times
+ -T Str override-tpl Override template file
+ - may not be preset
+ -l Str lib-template Library template file
+ - may appear multiple times
+ Str definitions Definitions input file
+ - disabled as --no-definitions
+ - enabled by default
+ - may not be preset
+ -S Str load-scheme Scheme code file to load
+ -F Str load-functions Load scheme function library
+ Str shell name or path name of shell to use
+ -m no no-fmemopen Do not use in-mem streams
+ Str equate characters considered equivalent
+
+The following options modify how output is handled:
+
+ Flg Arg Option-Name Description
+ -b Str base-name Base name for output file(s)
+ - may not be preset
+ no source-time set mod times to latest source
+ - disabled as --no-source-time
+ no writable Allow output files to be writable
+ - disabled as --not-writable
+
+The following options are often useful while debugging new templates:
+
+ Flg Arg Option-Name Description
+ Num loop-limit Limit on increment loops
+ - is scalable with a suffix: k/K/m/M/g/G/t/T
+ - It must lie in one of the ranges:
+ -1 exactly, or
+ 1 to 16777216
+ -t Num timeout Time limit for server shell
+ - It must be in the range:
+ 0 to 3600
+ KWd trace tracing level of detail
+ Str trace-out tracing output file or filter
+ --- show-defs This option has been disabled
+ no used-defines Show the definitions used
+ - may not be preset
+ -C no core Leave a core dump on a failure exit
+
+These options can be used to control what gets processed in the
+definitions files and template files:
+
+ Flg Arg Option-Name Description
+ -s Str skip-suffix Omit the file with this suffix
+ - prohibits these options:
+ select-suffix
+ - may not be preset
+ - may appear multiple times
+ -o Str select-suffix specify this output suffix
+ - may not be preset
+ - may appear multiple times
+ -D Str define name to add to definition list
+ - may appear multiple times
+ -U Str undefine definition list removal pattern
+ - an alternate for define
+
+This option is used to automate dependency tracking:
+
+ Flg Arg Option-Name Description
+ -M opt make-dep emit make dependency file
+ - may not be preset
+ - may appear multiple times
+
+version, usage and configuration options:
+
+ Flg Arg Option-Name Description
+ -R Str reset-option Reset an option's state
+ -v opt version Output version information and exit
+ -? no help Display extended usage information and exit
+ -! no more-help Extended usage information passed thru pager
+ -u no usage Abbreviated usage to stdout
+ -> opt save-opts Save the option state to a config file
+ -< Str load-opts Load options from a config file
+ - disabled as --no-load-opts
+ - may appear multiple times
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+
+AutoGen creates text files from templates using external definitions.
+
+The following option preset mechanisms are supported:
+ - reading file $HOME
+ - reading file ./.autogenrc
+ - examining environment variables named AUTOGEN_*
+
+The valid "trace" option keywords are:
+ nothing debug-message server-shell templates block-macros
+ expressions everything
+ or an integer from 0 through 6
+
+AutoGen is a tool designed for generating program files that contain
+repetitive text with varied substitutions.
+Packaged by Bruce (2012-08-10)
+Report autogen bugs to bkorb@@gnu.org
+@end example
+@exampleindent 4
+
+@node autogen input-select
+@section input-select options
+The following options select definitions, templates and scheme functions to use.
+@subheading templ-dirs option (-L).
+@anchor{autogen templ-dirs}
+@cindex autogen-templ-dirs
+
+This is the ``template search directory list'' option.
+This option takes an argument string @file{dir}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+Add a directory to the list of directories to search when opening
+a template, either as the primary template or an included one.
+The last entry has the highest priority in the search list.
+That is to say, they are searched in reverse order.
+@subheading override-tpl option (-T).
+@anchor{autogen override-tpl}
+@cindex autogen-override-tpl
+
+This is the ``override template file'' option.
+This option takes an argument string @file{tpl-file}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+Definition files specify the standard template that is to be expanded.
+This option will override that name and expand a different template.
+@subheading lib-template option (-l).
+@anchor{autogen lib-template}
+@cindex autogen-lib-template
+
+This is the ``library template file'' option.
+This option takes an argument string @file{tpl-file}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+DEFINE macros are saved from this template file for use in processing
+the main macro file. Template text aside from the DEFINE macros is
+is ignored.
+@subheading definitions option.
+@anchor{autogen definitions}
+@cindex autogen-definitions
+
+This is the ``definitions input file'' option.
+This option takes an argument string @file{file}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+is enabled by default.
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+Use this argument to specify the input definitions file with a
+command line option. If you do not specify this option, then
+there must be a command line argument that specifies the file,
+even if only to specify stdin with a hyphen (@code{-}).
+Specify, @code{--no-definitions} when you wish to process
+a template without any active AutoGen definitions.
+@subheading load-scheme option (-S).
+@anchor{autogen load-scheme}
+@cindex autogen-load-scheme
+
+This is the ``scheme code file to load'' option.
+This option takes an argument string @file{file}.
+Use this option to pre-load Scheme scripts into the Guile
+interpreter before template processing begins.
+Please note that the AutoGen specific functions are not loaded
+until after argument processing. So, though they may be specified
+in lambda functions you define, they may not be invoked until after
+option processing is complete.
+@subheading load-functions option (-F).
+@anchor{autogen load-functions}
+@cindex autogen-load-functions
+
+This is the ``load scheme function library'' option.
+This option takes an argument string @file{file}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{HAVE_DLOPEN} during the compilation.
+@end itemize
+
+This option is used to load Guile-scheme functions. The automatically
+called initialization routine @code{scm_init} must be used to register
+these routines or data.
+@subheading shell option.
+@anchor{autogen shell}
+@cindex autogen-shell
+
+This is the ``name or path name of shell to use'' option.
+This option takes an argument string @file{shell}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SHELL_ENABLED} during the compilation.
+@end itemize
+
+By default, when AutoGen is built, the configuration is probed for a
+reasonable Bourne-like shell to use for shell script processing. If
+a particular template needs an alternate shell, it must be specified
+with this option on the command line, with an environment variable
+(@code{SHELL}) or in the configuration/initialization file.
+@subheading no-fmemopen option (-m).
+@anchor{autogen no-fmemopen}
+@cindex autogen-no-fmemopen
+
+This is the ``do not use in-mem streams'' option.
+If the local C library supports "@code{fopencookie(3GNU)}", or
+"@code{funopen(3BSD)}" then AutoGen prefers to use in-memory stream
+buffer opens instead of anonymous files. This may lead to problems
+if there is a shortage of virtual memory. If, for a particular
+application, you run out of memory, then specify this option.
+This is unlikely in a modern 64-bit virtual memory environment.
+
+On platforms without these functions, the option is accepted
+but ignored. @code{fmemopen(POSIX)} is not adequate because
+its string buffer is not reallocatable. @code{open_memstream(POSIX)}
+is @i{also} not adequate because the stream is only opened for
+output. AutoGen needs a reallocatable buffer available for both
+reading and writing.
+@subheading equate option.
+@anchor{autogen equate}
+@cindex autogen-equate
+
+This is the ``characters considered equivalent'' option.
+This option takes an argument string @file{char-list}.
+This option will alter the list of characters considered equivalent.
+The default are the three characters, "_-^". (The last is conventional
+on a Tandem/HP-NonStop, and I used to do a lot of work on Tandems.)
+@node autogen out-handling
+@section out-handling options
+The following options modify how output is handled.
+@subheading base-name option (-b).
+@anchor{autogen base-name}
+@cindex autogen-base-name
+
+This is the ``base name for output file(s)'' option.
+This option takes an argument string @file{name}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+A template may specify the exact name of the output file. Normally,
+it does not. Instead, the name is composed of the base name of the
+definitions file with suffixes appended. This option will override the
+base name derived from the definitions file name. This is required if
+there is no definitions file and advisable if definitions are being
+read from stdin. If the definitions are being read from standard in,
+the base name defaults to @file{stdin}. Any leading directory components
+in the name will be silently removed. If you wish the output file to
+appear in a particular directory, it is recommended that you "cd" into
+that directory first, or use directory names in the format specification
+for the output suffix lists, @xref{pseudo macro}.
+@subheading source-time option.
+@anchor{autogen source-time}
+@cindex autogen-source-time
+
+This is the ``set mod times to latest source'' option.
+If you stamp your output files with the @code{DNE} macro output, then
+your output files will always be different, even if the content has
+not really changed. If you use this option, then the modification
+time of the output files will change only if the input files change.
+This will help reduce unneeded builds.
+@subheading writable option.
+@anchor{autogen writable}
+@cindex autogen-writable
+
+This is the ``allow output files to be writable'' option.
+This option will leave output files writable.
+Normally, output files are read-only.
+@node autogen debug-tpl
+@section debug-tpl options
+The following options are often useful while debugging new templates.
+They specify limits that prevent the template from taking overly long
+or producing more output than expected.
+@subheading loop-limit option.
+@anchor{autogen loop-limit}
+@cindex autogen-loop-limit
+
+This is the ``limit on increment loops'' option.
+This option takes an argument number @file{lim}.
+This option prevents runaway loops. For example, if you accidentally
+specify, "FOR x (for-from 1) (for-to -1) (for-by 1)", it will take a
+long time to finish. If you do have more than 256 entries in tables,
+you will need to specify a new limit with this option.
+@subheading timeout option (-t).
+@anchor{autogen timeout}
+@cindex autogen-timeout
+
+This is the ``time limit for server shell'' option.
+This option takes an argument number @file{time-lim}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SHELL_ENABLED} during the compilation.
+@end itemize
+
+AutoGen works with a shell server process. Most normal commands will
+complete in less than 10 seconds. If, however, your commands need more
+time than this, use this option.
+
+The valid range is 0 to 3600 seconds (1 hour).
+Zero will disable the server time limit.
+@subheading trace option.
+@anchor{autogen trace}
+@cindex autogen-trace
+
+This is the ``tracing level of detail'' option.
+This option takes an argument keyword @file{level}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+This option takes a keyword as its argument.
+The argument sets an enumeration value that can be tested by comparing the option value macro (OPT_VALUE_TRACE).
+The available keywords are:
+@example
+ nothing debug-message server-shell
+ templates block-macros expressions
+ everything
+@end example
+
+or their numeric equivalent.@end itemize
+
+This option will cause AutoGen to display a trace of its template
+processing. There are six levels, each level including messages from
+the previous levels:
+
+@table @samp
+@item nothing
+Does no tracing at all (default)
+
+@item debug-message
+Print messages from the "DEBUG" AutoGen macro (@pxref{DEBUG}).
+
+@item server-shell
+Traces all input and output to the server shell. This includes a shell
+"independent" initialization script about 30 lines long. Its output is
+discarded and not inserted into any template.
+
+@item templates
+Traces the invocation of @code{DEFINE}d macros and @code{INCLUDE}s
+
+@item block-macros
+Traces all block macros. The above, plus @code{IF}, @code{FOR},
+@code{CASE} and @code{WHILE}.
+
+@item expressions
+Displays the results of expression evaluations.
+
+@item everything
+Displays the invocation of every AutoGen macro, even @code{TEXT} macros
+(i.e. the text outside of macro quotes). Additionally, if you rebuild
+the ``expr.ini'' file with debugging enabled, then all calls to
+AutoGen defined scheme functions will also get logged:
+@*
+@example
+cd $@{top_builddir@}/agen5
+DEBUG_ENABLED=true bash bootstrap.dir expr.ini
+make CFLAGS='-g -DDEBUG_ENABLED=1'
+@end example
+
+Be aware that you cannot rebuild this source in this way without first
+having installed the @code{autogen} executable in your search path.
+Because of this, "expr.ini" is in the distributed source list, and
+not in the dependencies.
+@end table
+@subheading trace-out option.
+@anchor{autogen trace-out}
+@cindex autogen-trace-out
+
+This is the ``tracing output file or filter'' option.
+This option takes an argument string @file{file}.
+The output specified may be a file name, a file that is appended to,
+or, if the option argument begins with the @code{pipe} operator
+(@code{|}), a command that will receive the tracing output as standard
+in. For example, @code{--traceout='| less'} will run the trace output
+through the @code{less} program. Appending to a file is specified by
+preceeding the file name with two greater-than characters (@code{>>}).
+@subheading show-defs option.
+@anchor{autogen show-defs}
+@cindex autogen-show-defs
+
+This is the ``show the definition tree'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{DEBUG_ENABLED} during the compilation.
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+This will print out the complete definition tree before processing
+the template.
+@subheading used-defines option.
+@anchor{autogen used-defines}
+@cindex autogen-used-defines
+
+This is the ``show the definitions used'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+This will print out the names of definition values searched for
+during the processing of the template, whether actually found or
+not. There may be other referenced definitions in a template in
+portions of the template not evaluated. Some of the names listed
+may be computed names and others AutoGen macro arguments. This is
+not a means for producing a definitive, all-encompassing list of all
+and only the values used from a definition file. This is intended
+as an aid to template documentation only.
+@subheading core option (-C).
+@anchor{autogen core}
+@cindex autogen-core
+
+This is the ``leave a core dump on a failure exit'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{HAVE_SYS_RESOURCE_H} during the compilation.
+@end itemize
+
+Many systems default to a zero sized core limit. If the system
+has the sys/resource.h header and if this option is supplied,
+then in the failure exit path, autogen will attempt to set the
+soft core limit to whatever the hard core limit is. If that
+does not work, then an administrator must raise the hard core
+size limit.
+@node autogen processing
+@section processing options
+These options can be used to control what gets processed
+in the definitions files and template files.
+They specify which outputs and parts of outputs to produce.
+@subheading skip-suffix option (-s).
+@anchor{autogen skip-suffix}
+@cindex autogen-skip-suffix
+
+This is the ``omit the file with this suffix'' option.
+This option takes an argument string @file{suffix}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@item
+must not appear in combination with any of the following options:
+select-suffix.
+@end itemize
+
+Occasionally, it may not be desirable to produce all of the output
+files specified in the template. (For example, only the @file{.h}
+header file, but not the @file{.c} program text.) To do this
+specify @code{--skip-suffix=c} on the command line.
+@subheading select-suffix option (-o).
+@anchor{autogen select-suffix}
+@cindex autogen-select-suffix
+
+This is the ``specify this output suffix'' option.
+This option takes an argument string @file{suffix}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+If you wish to override the suffix specifications in the template,
+you can use one or more copies of this option. See the suffix
+specification in the @ref{pseudo macro} section of the info doc.
+@subheading define option (-D).
+@anchor{autogen define}
+@cindex autogen-define
+
+This is the ``name to add to definition list'' option.
+This option takes an argument string @file{value}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+The AutoGen define names are used for the following purposes:
+
+@enumerate
+@item
+Sections of the AutoGen definitions may be enabled or disabled
+by using C-style #ifdef and #ifndef directives.
+@item
+When defining a value for a name, you may specify the index
+for a particular value. That index may be a literal value,
+a define option or a value #define-d in the definitions themselves.
+@item
+The name of a file may be prefixed with @code{$NAME/}.
+The @code{$NAME} part of the name string will be replaced with
+the define-d value for @code{NAME}.
+@item
+When AutoGen is finished loading the definitions, the defined values
+are exported to the environment with, @code{putenv(3)}.
+These values can then be used in shell scripts with @code{$@{NAME@}}
+references and in templates with @code{(getenv "NAME")}.
+@item
+While processing a template, you may specify an index to retrieve
+a specific value. That index may also be a define-d value.
+@end enumerate
+
+It is entirely equivalent to place this name in the exported environment.
+Internally, that is what AutoGen actually does with this option.
+@subheading undefine option (-U).
+@anchor{autogen undefine}
+@cindex autogen-undefine
+
+This is the ``definition list removal pattern'' option.
+This option takes an argument string @file{name-pat}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+Similar to 'C', AutoGen uses @code{#ifdef/#ifndef} preprocessing
+directives. This option will cause the matching names to be
+removed from the list of defined values.
+@node autogen dep-track
+@section dep-track options
+This option is used to automate dependency tracking.
+@subheading make-dep option (-M).
+@anchor{autogen make-dep}
+@cindex autogen-make-dep
+
+This is the ``emit make dependency file'' option.
+This option takes an optional argument string @file{type}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+may not be preset with environment variables or configuration (rc/ini) files.
+@end itemize
+
+
+This option behaves fairly closely to the way the @code{-M} series of
+options work with the gcc compiler, except that instead of just
+emitting the predecessor dependencies, this also emits the successor
+dependencies (output target files). By default, the output dependency
+information will be placed in @code{<base-name>.d}, but may also be
+specified with @code{-MF<file>}. The time stamp on this file will be
+manipulated so that it will be one second older than the oldest
+primary output file.
+
+The target in this dependency file will normally be the dependency
+file name, but may also be overridden with @code{-MT<targ-name>}.
+AutoGen will not alter the contents of that file, but it may create
+it and it will adjust the modification time to match the start time.
+
+@strong{NB:} these second letters are part of the option argument, so
+@code{-MF <file>} must have the space character quoted or omitted, and
+@code{-M "F <file>"} is acceptable because the @code{F} is part of the
+option argument.
+
+@code{-M} may be followed by any of the letters M, F, P, T, Q, D, or G.
+However, only F, Q, T and P are meaningful. All but F have somewhat
+different meanings. @code{-MT<name>} is interpreted as meaning
+@code{<name>} is a sentinel file that will depend on all inputs
+(templates and definition files) and all the output files will depend
+on this sentinel file. It is suitable for use as a real make target.
+Q is treated identically to T, except dollar characters ('$') are
+doubled. P causes a special clean (clobber) phoney rule to be inserted
+into the make file fragment. An empty rule is always created for
+building the list of targets.
+
+This is the recommended usage:
+@example
+ -MFwhatever-you-like.dep -MTyour-sentinel-file -MP
+@end example
+and then in your @code{Makefile}, make the @file{autogen} rule:
+@example
+ -include whatever-you-like.dep
+ clean_targets += clean-your-sentinel-file
+
+ your-sentinel-file:
+ autogen -MT$@@ -MF$*.d .....
+
+ local-clean :
+ rm -f $(clean_targets)
+@end example
+
+The modification time on the dependency file is adjusted to be one
+second before the earliest time stamp of any other output file.
+Consequently, it is suitable for use as the sentinel file testifying
+to the fact the program was successfully run. (@code{-include} is
+the GNU make way of specifying "include it if it exists". Your make
+must support that feature or your bootstrap process must create the
+file.)
+
+All of this may also be specified using the @code{DEPENDENCIES_OUTPUT}
+or @code{AUTOGEN_MAKE_DEP} environment variables. If defined,
+dependency information will be output. If defined with white space
+free text that is something other than @code{true}, @code{false},
+@code{yes}, @code{no}, @code{0} or @code{1}, then the string is taken
+to be an output file name. If it contains a string of white space
+characters, the first token is as above and the second token is taken
+to be the target (sentinel) file as @code{-MT} in the paragraphs
+above. @code{DEPENDENCIES_OUTPUT} will be ignored if there are
+multiple sequences of white space characters or if its contents are,
+specifically, @code{false}, @code{no} or @code{0}.
+
+
+@node autogen config
+@section presetting/configuring autogen
+
+Any option that is not marked as @i{not presettable} may be preset by
+loading values from configuration ("rc" or "ini") files, and values from environment variables named @code{AUTOGEN} and @code{AUTOGEN_<OPTION_NAME>}. @code{<OPTION_NAME>} must be one of
+the options listed above in upper case and segmented with underscores.
+The @code{AUTOGEN} variable will be tokenized and parsed like
+the command line. The remaining variables are tested for existence and their
+values are treated like option arguments.
+
+
+@noindent
+@code{libopts} will search in 2 places for configuration files:
+@itemize @bullet
+@item
+$HOME
+@item
+$PWD
+@end itemize
+The environment variables @code{HOME}, and @code{PWD}
+are expanded and replaced when @file{autogen} runs.
+For any of these that are plain files, they are simply processed.
+For any that are directories, then a file named @file{.autogenrc} is searched for
+within that directory and processed.
+
+
+Configuration files may be in a wide variety of formats.
+The basic format is an option name followed by a value (argument) on the
+same line. Values may be separated from the option name with a colon,
+equal sign or simply white space. Values may be continued across multiple
+lines by escaping the newline with a backslash.
+
+Multiple programs may also share the same initialization file.
+Common options are collected at the top, followed by program specific
+segments. The segments are separated by lines like:
+@example
+[AUTOGEN]
+@end example
+@noindent
+or by
+@example
+<?program autogen>
+@end example
+@noindent
+Do not mix these styles within one configuration file.
+
+Compound values and carefully constructed string values may also be
+specified using XML syntax:
+@example
+<option-name>
+ <sub-opt>...&lt;...&gt;...</sub-opt>
+</option-name>
+@end example
+@noindent
+yielding an @code{option-name.sub-opt} string value of
+@example
+"...<...>..."
+@end example
+@code{AutoOpts} does not track suboptions. You simply note that it is a
+hierarchicly valued option. @code{AutoOpts} does provide a means for searching
+the associated name/value pair list (see: optionFindValue).
+
+The command line options relating to configuration and/or usage help are:
+
+@subheading version (-v)
+
+Print the program version to standard out, optionally with licensing
+information, then exit 0. The optional argument specifies how much licensing
+detail to provide. The default is to print just the version. The licensing infomation may be selected with an option argument. Only the
+first letter of the argument is examined:
+
+@table @samp
+@item version
+Only print the version. This is the default.
+@item copyright
+Name the copyright usage licensing terms.
+@item verbose
+Print the full copyright usage licensing terms.
+@end table
+
+@subheading usage (-u)
+
+Print abbreviated usage to standard out, then exit 0.
+
+@subheading reset-option (-R)
+
+Resets the specified option to the compiled-in initial state.
+This will undo anything that may have been set by configuration files.
+The option argument may be either the option flag character or its long name.
+
+@node autogen exit status
+@section autogen exit status
+
+One of the following exit values will be returned:
+@table @samp
+@item 0 (EXIT_SUCCESS)
+Successful program execution.
+@item 1 (EXIT_OPTION_ERROR)
+The command options were misconfigured.
+@item 2 (EXIT_BAD_TEMPLATE)
+An error was encountered processing the template.
+@item 3 (EXIT_BAD_DEFINITIONS)
+The definitions could not be deciphered.
+@item 4 (EXIT_LOAD_ERROR)
+An error was encountered during the load phase.
+@item 5 (EXIT_SIGNAL)
+Program exited due to catching a signal. If your template includes
+string formatting, a number argument to a "%s" formatting element will
+trigger a segmentation fault. Autogen will catch the seg fault signal
+and exit with @code{AUTOGEN_EXIT_SIGNAL(5)}. Alternatively, AutoGen
+may have been interrupted with a @code{kill(2)} signal.
+@item 66 (EX_NOINPUT)
+A specified configuration file could not be loaded.
+@item 70 (EX_SOFTWARE)
+libopts had an internal operational error. Please report
+it to autogen-users@@lists.sourceforge.net. Thank you.
+@end table
+@node autogen Examples
+@section autogen Examples
+Here is how the man page is produced:
+@example
+autogen -Tagman-cmd.tpl -MFman-dep -MTstamp-man opts.def
+@end example
+
+This command produced this man page from the AutoGen option definition
+file. It overrides the template specified in @file{opts.def} (normally
+@file{options.tpl}) and uses @file{agman-cmd.tpl}. It also sets the
+make file dependency output to @file{man-dep} and the sentinel file
+(time stamp file) to @file{man-stamp}. The base of the file name is
+derived from the defined @code{prog-name}.
+
+The texi invocation document is produced via:
+@example
+autogen -Tagtexi-cmd.tpl -MFtexi-dep -MTtexi-stamp opts.def
+@end example
+
diff --git a/agen5/loadPseudo.c b/agen5/loadPseudo.c
new file mode 100644
index 0000000..33763da
--- /dev/null
+++ b/agen5/loadPseudo.c
@@ -0,0 +1,497 @@
+
+/**
+ * @file loadPseudo.c
+ *
+ * Find the start and end macro markers. In btween we must find the
+ * "autogen" and "template" keywords, followed by any suffix specs.
+ *
+ * Time-stamp: "2012-03-31 13:03:19 bkorb"
+ *
+ * This module processes the "pseudo" macro
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DEFINE_FSM
+#include "pseudo-fsm.h"
+
+/* = = = START-STATIC-FORWARD = = = */
+static char const *
+do_scheme_expr(char const * pzData, char const * pzFileName);
+
+static char const *
+handle_hash_line(char const * pz);
+
+static te_pm_event
+next_pm_token(char const ** ppzData, te_pm_state fsm_state, char const * fnm);
+
+static char const *
+copy_mark(char const * pzData, char* pzMark, size_t * pCt);
+/* = = = END-STATIC-FORWARD = = = */
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * do_scheme_expr
+ *
+ * Process a scheme specification
+ */
+static char const *
+do_scheme_expr(char const * pzData, char const * pzFileName)
+{
+ char* pzEnd = (char*)pzData + strlen(pzData);
+ char ch;
+ macro_t* pCM = cur_macro;
+ macro_t mac = { (mac_func_t)~0, 0, 0, 0, 0, 0, 0, NULL };
+
+ mac.md_line = tpl_line;
+ pzEnd = (char*)skip_scheme(pzData, pzEnd);
+ ch = *pzEnd;
+ *pzEnd = NUL;
+ cur_macro = &mac;
+
+ ag_scm_c_eval_string_from_file_line(
+ (char*)pzData, pzFileName, tpl_line );
+
+ cur_macro = pCM;
+ *pzEnd = ch;
+ while (pzData < pzEnd)
+ if (*(pzData++) == NL)
+ tpl_line++;
+ return (char const *)pzEnd;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * do_suffix
+ *
+ * Process a suffix specification
+ */
+LOCAL char const *
+do_suffix(char const * const pzData, char const * pzFileName, int lineNo)
+{
+ /*
+ * The following is the complete list of POSIX required-to-be-legal
+ * file name characters. These are the only characters we allow to
+ * appear in a suffix. We do, however, add '=' and '%' because we
+ * also allow a format specification to follow the suffix,
+ * separated by an '=' character.
+ */
+ out_spec_t * pOS;
+ char const * pzSfxFmt;
+ char const * pzResult;
+ size_t spn;
+ static out_spec_t ** ppOSList = &output_specs;
+
+ /*
+ * Skip over the suffix construct
+ */
+ pzSfxFmt = SPN_SUFFIX_CHARS(pzData);
+
+ if (*pzSfxFmt != '=') {
+ pzResult = pzSfxFmt;
+ pzSfxFmt = NULL;
+
+ } else {
+ pzSfxFmt++;
+
+ if (*pzSfxFmt == '(') {
+ char const *pe = pzSfxFmt + strlen(pzSfxFmt);
+ pzResult = skip_scheme(pzSfxFmt, pe);
+
+ } else {
+ pzResult = SPN_SUFFIX_FMT_CHARS(pzSfxFmt);
+
+ if (pzSfxFmt == pzResult)
+ AG_ABEND(DO_SUFFIX_EMPTY);
+ }
+ }
+
+ /*
+ * If pzFileName is NULL, then we are called by --select-suffix.
+ * Otherwise, the suffix construct is saved only for the main template,
+ * and only when the --select-suffix option was not specified.
+ */
+ if ( (pzFileName != NULL)
+ && ( (processing_state != PROC_STATE_LOAD_TPL)
+ || HAVE_OPT(SELECT_SUFFIX)))
+ return pzResult;
+
+ /*
+ * Allocate Output Spec and link into the global list. Copy all the
+ * "spanned" text, including any '=' character, scheme expression or
+ * file name format string.
+ */
+ spn = pzResult - pzData;
+ {
+ size_t sz = sizeof(*pOS) + spn + 1;
+ pOS = AGALOC(sz, "Output Specification");
+ memset(pOS, NUL, sz);
+ }
+
+ *ppOSList = pOS;
+ ppOSList = &pOS->os_next; /* NULL, from memset */
+ memcpy(pOS->os_sfx, pzData, spn);
+ pOS->os_sfx[spn] = NUL;
+
+ /*
+ * IF the suffix contains its own formatting construct,
+ * THEN split it off from the suffix and set the formatting ptr.
+ * ELSE supply a default.
+ */
+ if (pzSfxFmt != NULL) {
+ size_t sfx_len = pzSfxFmt - pzData;
+ pOS->os_sfx[sfx_len-1] = NUL;
+ pOS->os_file_fmt = pOS->os_sfx + sfx_len;
+
+ if (*pOS->os_file_fmt == '(') {
+ SCM str =
+ ag_scm_c_eval_string_from_file_line(
+ pOS->os_file_fmt, pzFileName, lineNo );
+ size_t str_length;
+ char const * pz;
+
+ pzSfxFmt = pz = scm2display(str);
+ str_length = strlen(pzSfxFmt);
+
+ if (str_length == 0)
+ AG_ABEND(DO_SUFFIX_EMPTY);
+ pz = SPN_SUFFIX_FMT_CHARS(pz);
+
+ if ((unsigned)(pz - pzSfxFmt) != str_length)
+ AG_ABEND(aprf(DO_SUFFIX_BAD_CHARS, pz));
+
+ /*
+ * IF the scheme replacement text fits in the space, don't
+ * mess with allocating another string.
+ */
+ if (str_length < spn - sfx_len)
+ strcpy(pOS->os_sfx + sfx_len, pzSfxFmt);
+ else {
+ AGDUPSTR(pOS->os_file_fmt, pzSfxFmt, "suffix format");
+ pOS->os_dealloc_fmt = true;
+ }
+ }
+
+ } else {
+ /*
+ * IF the suffix does not start with punctuation,
+ * THEN we will insert a '.' of our own.
+ */
+ pOS->os_file_fmt = IS_VAR_FIRST_CHAR(pOS->os_sfx[0])
+ ? DOT_SFX_FMT : SFX_FMT;
+ }
+
+ return pzResult;
+}
+
+static char const *
+handle_hash_line(char const * pz)
+{
+ char const * res = strchr(pz, NL);
+ if (res == NULL)
+ AG_ABEND(HANDLE_HASH_BAD_TPL);
+
+ /*
+ * If the comment starts with "#!/", then see if it names
+ * an executable. If it does, it is specifying a shell to use.
+ */
+ if ((pz[1] == '!') && (pz[2] == '/')) {
+ char const * pzScn = pz + 3;
+ char * nmbuf;
+ size_t len;
+
+ pzScn = SPN_FILE_NAME_CHARS(pzScn);
+
+ len = pzScn - (pz + 2);
+ nmbuf = ag_scribble(len);
+ memcpy(nmbuf, pz+2, len);
+ nmbuf[len] = NUL;
+
+ /*
+ * If we find the executable, then change the configured shell and
+ * the SHELL environment variable to this executable.
+ */
+ if (access(nmbuf, X_OK) == 0) {
+ char * sp = malloc(len + 7); // len + "SHELL=" + NUL byte
+ sprintf(sp, HANDLE_HASH_ENV_FMT, HANDLE_HASH_SHELL, nmbuf);
+ putenv(sp);
+ AGDUPSTR(shell_program, nmbuf, "prog shell");
+ AGDUPSTR(server_args[0], nmbuf, "shell name");
+ }
+ }
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * next_pm_token
+ *
+ * Skiping leading white space, figure out what sort of token is under
+ * the scan pointer (pzData).
+ */
+static te_pm_event
+next_pm_token(char const ** ppzData, te_pm_state fsm_state, char const * fnm)
+{
+ char const * pzData = *ppzData;
+
+ /*
+ * At the start of processing in this function, we can never be at
+ * the "start of a line". A '#' type comment before the initial
+ * start macro marker is illegal. Otherwise, our scan pointer is
+ * after some valid token, which won't be the start of a line, either.
+ */
+ bool line_start = false;
+
+ skipWhiteSpace:
+ while (IS_WHITESPACE_CHAR(*pzData)) {
+ if (*(pzData++) == NL) {
+ line_start = true;
+ tpl_line++;
+
+ /*
+ * IF we are done with the macro markers,
+ * THEN we skip white space only thru the first new line.
+ */
+ if (fsm_state == PM_ST_END_MARK) {
+ *ppzData = pzData;
+ return PM_EV_END_PSEUDO;
+ }
+ }
+ }
+
+ if (line_start && (*pzData == '#')) {
+ pzData = handle_hash_line(pzData);
+ goto skipWhiteSpace;
+ }
+
+ *ppzData = pzData; /* in case we return */
+
+ /*
+ * After the end marker has been found,
+ * anything else is really the start of the data.
+ */
+ if (fsm_state == PM_ST_END_MARK)
+ return PM_EV_END_PSEUDO;
+
+ /*
+ * IF the token starts with an alphanumeric,
+ * THEN it must be "autogen5" or "template" or a suffix specification
+ */
+ if (IS_VAR_FIRST_CHAR(*pzData)) {
+ if (strneqvcmp(pzData, AG_MARK, AG_MARK_LEN) == 0) {
+ if (IS_WHITESPACE_CHAR(pzData[ AG_MARK_LEN ])) {
+ *ppzData = pzData + AG_MARK_LEN + 1;
+ return PM_EV_AUTOGEN;
+ }
+
+ return PM_EV_SUFFIX;
+ }
+
+ if ( (strneqvcmp(pzData, TPL_MARK, TPL_MARK_LEN) == 0)
+ && (IS_WHITESPACE_CHAR(pzData[ TPL_MARK_LEN ])) ) {
+ *ppzData = pzData + TPL_MARK_LEN;
+ return PM_EV_TEMPLATE;
+ }
+
+ return PM_EV_SUFFIX;
+ }
+
+ /*
+ * Handle emacs mode markers and scheme expressions only once we've
+ * gotten past "init" state.
+ */
+ if (fsm_state > PM_ST_INIT)
+ switch (*pzData) {
+ case '-':
+ if ((pzData[1] == '*') && (pzData[2] == '-'))
+ return PM_EV_ED_MODE;
+ break;
+
+ case '(':
+ return PM_EV_SCHEME;
+ }
+
+ /*
+ * Alphanumerics and underscore are already handled. Thus, it must be
+ * a punctuation character that may introduce a suffix: '.' '-' '_'
+ */
+ if (IS_SUFFIX_CHAR(*pzData))
+ return PM_EV_SUFFIX;
+
+ /*
+ * IF it is some other punctuation,
+ * THEN it must be a start/end marker.
+ */
+ if (IS_PUNCTUATION_CHAR(*pzData))
+ return PM_EV_MARKER;
+
+ /*
+ * Otherwise, it is just junk.
+ */
+ AG_ABEND(aprf(NEXT_PM_TOKEN_INVALID, fnm));
+ /* NOTREACHED */
+ return PM_EV_INVALID;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Some sort of marker is under the scan pointer. Copy it for as long
+ * as we find punctuation characters.
+ */
+static char const *
+copy_mark(char const * pzData, char* pzMark, size_t * pCt)
+{
+ int ct = 0;
+
+ for (;;) {
+ char ch = *pzData;
+ if (! IS_PUNCTUATION_CHAR(ch))
+ break;
+ *(pzMark++) = ch;
+ if (++ct >= (int)sizeof(st_mac_mark))
+ return NULL;
+
+ pzData++;
+ }
+
+ *pCt = ct;
+ *pzMark = NUL;
+
+ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
+ fprintf(trace_fp, TRACE_COPY_MARK, pzMark - ct);
+
+ return pzData;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Using a finite state machine, scan over the tokens that make up the
+ * "pseudo macro" at the start of every template.
+ */
+LOCAL char const *
+loadPseudoMacro(char const * pzData, char const * pzFileName)
+{
+ char const * pzBadness;
+# define BAD_MARKER(t) { pzBadness = t; goto abort_load; }
+
+ te_pm_state fsm_state = PM_ST_INIT;
+
+ tpl_line = 1;
+
+ while (fsm_state != PM_ST_DONE) {
+ te_pm_event fsm_tkn = next_pm_token(&pzData, fsm_state, pzFileName);
+ te_pm_state nxt_state;
+ te_pm_trans trans;
+
+ nxt_state = pm_trans_table[ fsm_state ][ fsm_tkn ].next_state;
+ trans = pm_trans_table[ fsm_state ][ fsm_tkn ].transition;
+
+ /*
+ * There are only so many "PM_TR_<state-name>_<token-name>"
+ * transitions that are legal. See which one we got.
+ * It is legal to alter "nxt_state" while processing these.
+ */
+ switch (trans) {
+ case PM_TR_SKIP_ED_MODE:
+ {
+ char* pzEnd = strstr(pzData + 3, PSEUDO_MAC_MODE_MARK);
+ char* pzNL = strchr(pzData + 3, NL);
+ if ((pzEnd == NULL) || (pzNL < pzEnd))
+ BAD_MARKER(PSEUDO_MAC_BAD_MODE);
+
+ pzData = pzEnd + 3;
+ break;
+ }
+
+ case PM_TR_INIT_MARKER:
+ pzData = copy_mark(pzData, st_mac_mark, &st_mac_len);
+ if (pzData == NULL)
+ BAD_MARKER(PSEUDO_MAC_BAD_LENGTH);
+
+ break;
+
+ case PM_TR_TEMPL_MARKER:
+ pzData = copy_mark(pzData, end_mac_mark, &end_mac_len);
+ if (pzData == NULL)
+ BAD_MARKER(PSEUDO_MAC_BAD_LENGTH);
+
+ /*
+ * IF the end macro seems to end with the start macro and
+ * it is exactly twice as long as the start macro, then
+ * presume that someone ran the two markers together.
+ */
+ if ( (end_mac_len == 2 * st_mac_len)
+ && (strcmp(st_mac_mark, end_mac_mark + st_mac_len) == 0)) {
+ pzData -= st_mac_len;
+ end_mac_mark[ st_mac_len ] = NUL;
+ end_mac_len = st_mac_len;
+ }
+
+ if (strstr(end_mac_mark, st_mac_mark) != NULL)
+ BAD_MARKER(PSEUDO_MAC_BAD_ENDER);
+ if (strstr(st_mac_mark, end_mac_mark) != NULL)
+ BAD_MARKER(PSEUDO_MAC_BAD_STARTER);
+ break;
+
+ case PM_TR_TEMPL_SUFFIX:
+ pzData = do_suffix(pzData, pzFileName, tpl_line);
+ break;
+
+ case PM_TR_TEMPL_SCHEME:
+ pzData = do_scheme_expr(pzData, pzFileName);
+ break;
+
+ case PM_TR_INVALID:
+ pm_invalid_transition(fsm_state, fsm_tkn);
+ switch (fsm_state) {
+ case PM_ST_INIT: BAD_MARKER(PSEUDO_MAC_BAD_NOSTART);
+ case PM_ST_ST_MARK: BAD_MARKER(PSEUDO_MAC_BAD_NOAG5);
+ case PM_ST_AGEN: BAD_MARKER(PSEUDO_MAC_BAD_NOTPL);
+ case PM_ST_TEMPL: BAD_MARKER(PSEUDO_MAC_BAD_NOEND);
+ case PM_ST_END_MARK: BAD_MARKER(PSEUDO_MAC_BAD_NOEOL);
+ default: BAD_MARKER(PSEUDO_MAC_BAD_FSM);
+ }
+
+ case PM_TR_NOOP:
+ break;
+
+ default:
+ BAD_MARKER(PSEUDO_MAC_BAD_PSEUDO);
+ }
+
+ fsm_state = nxt_state;
+ }
+
+ return pzData;
+
+ abort_load:
+ AG_ABEND(aprf(PSEUDO_MAC_ERR_FMT, pzFileName, tpl_line, pzBadness));
+# undef BAD_MARKER
+ return NULL;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/loadPseudo.c */
diff --git a/agen5/mk-stamps.sh b/agen5/mk-stamps.sh
new file mode 100644
index 0000000..1c841f5
--- /dev/null
+++ b/agen5/mk-stamps.sh
@@ -0,0 +1,298 @@
+#! /bin/echo This_file_must_be_sourced,_not_executed
+#
+# agen5/mk-stamps.sh
+#
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+# ----------------------------------------------------------------------
+#
+# This script rebuilds sources not kept in the GIT repository.
+# These files are distributed, so it is not necessary to invoke
+# AutoGen before building AutoGen.
+
+# "make" will invoke this file with the name of the desired output
+# as an argument. We do this here rather than in the make file
+# because some of the rules are complex and we don't want to
+# deal with the dual update problem.
+
+if test -z "$mainpid"
+then
+ . ${top_srcdir}/config/bootstrap.shlib
+ . ${top_builddir}/config/shdefs
+fi
+
+set_defaults()
+{
+ local AGnam=autogen
+ local GDnam=getdefs
+ local CLnam=columns
+ local AGdir=agen5
+ local GDdir=getdefs
+ local CLdir=columns
+ local f n b v
+
+ for v in AG GD CL
+ do
+ eval f=\${${v}exe}
+ eval n=\${${v}nam}
+ eval b=${top_builddir}/\${${v}dir}/\${${v}nam}
+ if test -x "$b"
+ then
+ f=$b
+
+ elif ! test -x "$f"
+ then
+ f=`command -v $n`
+ test -x "${f}" || die "$n is required"
+ fi
+ eval $v=$f
+ export $v
+ done
+
+ if ! test -x ${AGexe}
+ then
+ run_ag() {
+ touch stamp-${1}
+ touch ${target}
+ }
+ fi
+
+ # Make sure we have a default for top build and source.
+ # Some of the templates need this information.
+ #
+ local verdata=`${EGREP} '^AG_' ${top_srcdir}/VERSION | \
+ sed 's,^\(AG[^=]*\)\(.*\),\1\2 export \1,'`
+ eval "${verdata}"
+
+ # disable any $HOME defaults
+ #
+ HOME=/dev/null
+ SHELL=${POSIX_SHELL-/bin/sh}
+
+ ${VERBOSE:-false} && set -x || :
+ # Ensure complete success or a noticable failure
+ #
+ set -e
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Make the definition parser
+#
+make_parse()
+{
+ local opts=''
+
+ test ${#DEBUG_ENABLED} -gt 0 && \
+ opts=-DDEBUG_ENABLED
+
+ run_ag parse ${opts} defParse.def
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Make the pseudo-macro processing Finite State Machine
+#
+make_cgi()
+{
+ run_ag cgi cgi.def
+}
+
+make_pseudo()
+{
+ run_ag pseudo pseudo.def
+ rm -f .fsm.*
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Make the expression processing code
+#
+# NOTE: the expr.test test extracts this function!!
+#
+make_exprini()
+{
+ # funcCase.c must be first file in list.
+ # It has the exparg attribute names.
+ #
+ exec 3> expr.cfg
+ cat >&3 <<- _EOConfig_
+ defs-to-get gfunc
+ template snarf
+ srcfile
+ assign two-phase = yes
+ assign group = ag
+ output expr.def
+ subblock exparg = arg_name,arg_desc,arg_optional,arg_list
+ _EOConfig_
+
+ test ${#DEBUG_ENABLED} -gt 0 && \
+ printf '%-15s %s\n' assign 'debug-enabled = true' >&3
+
+ for f in func*.c exp*.c agShell.c
+ do echo input ${f} ; done >&3
+ exec 3>&-
+
+ echo ${GDexe} load=expr.cfg
+ set +e
+ ( SHELL=sh ${GDexe} load=expr.cfg ${files} 2>&1 ) | \
+ ${EGREP} -v 'no copies of pattern' >&2
+ set -e
+ run_ag exprini expr.def
+ rmlist=${rmlist}\ expr.cfg
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Make the directive.h header
+#
+make_directive()
+{
+ cat > directive.cfg <<- _EOConfig_
+ defs-to-get directive
+ template directive
+ output directive.def
+ linenum
+ _EOConfig_
+
+ ${GDexe} load=directive.cfg ${srcdir}/defDirect.c
+ run_ag directive -t30 directive.def
+ rmlist=${rmlist}\ directive.cfg
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Construct the texinfo doc
+#
+make_texi()
+{
+ eopt="-Tagtexi-cmd.tpl -DLEVEL=chapter -binvoke-autogen"
+ run_ag texi ${eopt} ${srcdir}/opts.def
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Construct the ag-text strings
+#
+make_ag_text()
+{
+ run_ag ag_text ${srcdir}/ag-text.def
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Construct the man page
+#
+make_fmem()
+{
+ ${GDexe} templ=agman3.tpl linenum output=fmemopen.def ${srcdir}/fmemopen.c
+ run_ag fmem fmemopen.def
+}
+
+make_man()
+{
+ run_ag man -Tagman-cmd -DMAN_SECTION=1 ${srcdir}/opts.def
+}
+
+# # # # # # # # # # # # # # # # # # #
+#
+# Construct the function tables
+#
+make_func()
+{
+ local files=`${GREP} -l '/\*=macfunc' *.c`
+ local opts='srcfile linenum defs=macfunc listattr=alias'
+
+ ${GDexe} output=functions.def template=functions.tpl ${opts} ${files}
+ run_ag func functions.def
+}
+
+make_gver()
+{
+ test -f guile-iface.def || make_func
+ run_ag gver guile-iface.def
+}
+
+dispatch()
+{
+ if test -d "${DEPDIR}"
+ then DEPFILE=./${DEPDIR}/stamp-${1}.d
+ else DEPFILE=''
+ fi
+ eval make_${1}
+}
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+#
+# M A I N
+#
+PS4='>stmp-${FUNCNAME}> '
+set_defaults ${1+"$@"}
+
+# FOR each output target,
+# DO the appropriate rule...
+#
+for t
+do
+ echo Re-building "$t"
+ case "$t" in
+ stamp-* )
+ dispatch ${t#stamp-} ;;
+
+ defParse-fsm.c | defParse-fsm.h )
+ dispatch parse ;;
+
+ opts.[ch] )
+ dispatch opts ;;
+
+ expr.h | expr.def | expr.ini )
+ dispatch exprini ;;
+
+ directive.def | directive.h )
+ dispatch directive ;;
+
+ autogen.texi | autogen.menu )
+ dispatch texi ;;
+
+ autogen.1 | fmemopen.3 )
+ dispatch man ;;
+
+ proto.h )
+ dispatch proto ;;
+
+ functions.h )
+ dispatch func ;;
+
+ *) if test `type -t $t` = function 2>/dev/null 1>&2
+ then dispatch $t
+ else die "Don't know how to make $t"
+ fi ;;
+ esac
+done
+assemble_Makefile
+
+rm -f $rmlist ag-*.log
+
+trap '' 0
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:4
+# sh-basic-offset:4
+# indent-tabs-mode: nil
+# End:
+
+# end of agen5/bootstrap.dir
diff --git a/agen5/opts.c b/agen5/opts.c
new file mode 100644
index 0000000..2e623f3
--- /dev/null
+++ b/agen5/opts.c
@@ -0,0 +1,1491 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (opts.c)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:32 AM by AutoGen 5.16.2pre7
+ * From the definitions opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 36:5:11 templates.
+ *
+ * AutoOpts is a copyrighted work. This source file is not encumbered
+ * by AutoOpts licensing, but is provided under the licensing terms chosen
+ * by the autogen author or copyright holder. AutoOpts is
+ * licensed under the terms of the LGPL. The redistributable library
+ * (``libopts'') is licensed under the terms of either the LGPL or, at the
+ * users discretion, the BSD license. See the AutoOpts and/or libopts sources
+ * for details.
+ *
+ * The autogen program is copyrighted and licensed
+ * under the following terms:
+ *
+ * Copyright (C) 1992-2012 Bruce Korb, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the
+ * GNU General Public License, version 3 or later
+ * <http://gnu.org/licenses/gpl.html>
+ *
+ * autogen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * autogen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __doxygen__
+#define OPTION_CODE_COMPILE 1
+#include "opts.h"
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern FILE * option_usage_fp;
+
+/* TRANSLATORS: choose the translation for option names wisely because you
+ cannot ever change your mind. */
+#define zCopyright (autogen_opt_strs+0)
+#define zLicenseDescrip (autogen_opt_strs+275)
+
+/*
+ * global included definitions
+ */
+
+#include "autogen.h"
+#ifdef HAVE_DLOPEN
+# ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+# else
+ extern void* dlopen(char const*,int);
+# endif
+
+# ifndef RTLD_GLOBAL
+# define RTLD_GLOBAL 0
+# endif
+
+# ifndef RTLD_NOW
+# ifdef RTLD_LAZY
+# define RTLD_NOW DL_LAZY
+# else
+# define RTLD_NOW 0
+# endif
+# endif
+#endif
+
+#if HAVE_CTYPE_H
+# include <ctype.h>
+#else
+# define isspace(_c) 0
+#endif
+
+typedef void (init_proc_t)(void);
+char const * tpl_fname = NULL;
+bool trace_is_to_pipe = false;
+
+#define CANNOT_LOCATE_FMT (autogen_opt_strs+878)
+#define LOAD_GUILE_FILE_FMT (autogen_opt_strs+910)
+
+#define DLOPEN_ERROR_FMT (autogen_opt_strs+922)
+#define SYM_NOT_FOUND_FMT (autogen_opt_strs+946)
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/*
+ * autogen option static const strings
+ */
+static char const autogen_opt_strs[3398] =
+/* 0 */ "autogen (GNU AutoGen) 5.16.2\n"
+ "Copyright (C) 1992-2012 Bruce Korb, all rights reserved.\n"
+ "This is free software. It is licensed for use, modification and\n"
+ "redistribution under the terms of the\n"
+ "GNU General Public License, version 3 or later\n"
+ " <http://gnu.org/licenses/gpl.html>\n\0"
+/* 275 */ "autogen is free software: you can redistribute it and/or modify it under\n"
+ "the terms of the GNU General Public License as published by the Free\n"
+ "Software Foundation, either version 3 of the License, or (at your option)\n"
+ "any later version.\n\n"
+ "autogen is distributed in the hope that it will be useful, but WITHOUT ANY\n"
+ "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n"
+ "FOR A PARTICULAR PURPOSE. See the GNU General Public License for more\n"
+ "details.\n\n"
+ "You should have received a copy of the GNU General Public License along\n"
+ "with this program. If not, see <http://www.gnu.org/licenses/>.\n\0"
+/* 878 */ "Cannot locate scheme file '%s'\n\0"
+/* 910 */ "(load \"%s\")\0"
+/* 922 */ "dlopen(%s) error %d\n"
+ "%s\n\0"
+/* 946 */ "dlsym(scm_init) not found in %s\n"
+ "\tyou must initialize the library yourself\n\0"
+/* 1021 */ "The following options select definitions, templates and scheme functions\n"
+ "to use:\0"
+/* 1102 */ "Template search directory list\0"
+/* 1133 */ "TEMPL_DIRS\0"
+/* 1144 */ "templ-dirs\0"
+/* 1155 */ "Override template file\0"
+/* 1178 */ "OVERRIDE_TPL\0"
+/* 1191 */ "override-tpl\0"
+/* 1204 */ "Library template file\0"
+/* 1226 */ "LIB_TEMPLATE\0"
+/* 1239 */ "lib-template\0"
+/* 1252 */ "Definitions input file\0"
+/* 1275 */ "DEFINITIONS\0"
+/* 1287 */ "no-definitions\0"
+/* 1302 */ "no\0"
+/* 1305 */ "Scheme code file to load\0"
+/* 1330 */ "LOAD_SCHEME\0"
+/* 1342 */ "load-scheme\0"
+/* 1354 */ "Load scheme function library\0"
+/* 1383 */ "LOAD_FUNCTIONS\0"
+/* 1398 */ "load-functions\0"
+/* 1413 */ "name or path name of shell to use\0"
+/* 1447 */ "SHELL\0"
+/* 1453 */ "shell\0"
+/* 1459 */ "Do not use in-mem streams\0"
+/* 1485 */ "NO_FMEMOPEN\0"
+/* 1497 */ "no-fmemopen\0"
+/* 1509 */ "characters considered equivalent\0"
+/* 1542 */ "EQUATE\0"
+/* 1549 */ "equate\0"
+/* 1556 */ "_-^\0"
+/* 1560 */ "The following options modify how output is handled:\0"
+/* 1612 */ "Base name for output file(s)\0"
+/* 1641 */ "BASE_NAME\0"
+/* 1651 */ "base-name\0"
+/* 1661 */ "set mod times to latest source\0"
+/* 1692 */ "SOURCE_TIME\0"
+/* 1704 */ "no-source-time\0"
+/* 1719 */ "Allow output files to be writable\0"
+/* 1753 */ "WRITABLE\0"
+/* 1762 */ "not-writable\0"
+/* 1775 */ "not\0"
+/* 1779 */ "The following options are often useful while debugging new templates:\0"
+/* 1849 */ "Limit on increment loops\0"
+/* 1874 */ "LOOP_LIMIT\0"
+/* 1885 */ "loop-limit\0"
+/* 1896 */ "Time limit for server shell\0"
+/* 1924 */ "TIMEOUT\0"
+/* 1932 */ "timeout\0"
+/* 1940 */ "tracing level of detail\0"
+/* 1964 */ "TRACE\0"
+/* 1970 */ "trace\0"
+/* 1976 */ "tracing output file or filter\0"
+/* 2006 */ "TRACE_OUT\0"
+/* 2016 */ "trace-out\0"
+/* 2026 */ "Show the definition tree\0"
+/* 2051 */ "SHOW_DEFS\0"
+/* 2061 */ "show-defs\0"
+/* 2071 */ "Show the definitions used\0"
+/* 2097 */ "USED_DEFINES\0"
+/* 2110 */ "used-defines\0"
+/* 2123 */ "Leave a core dump on a failure exit\0"
+/* 2159 */ "CORE\0"
+/* 2164 */ "core\0"
+/* 2169 */ "These options can be used to control what gets processed in the\n"
+ "definitions files and template files:\0"
+/* 2271 */ "Omit the file with this suffix\0"
+/* 2302 */ "SKIP_SUFFIX\0"
+/* 2314 */ "skip-suffix\0"
+/* 2326 */ "specify this output suffix\0"
+/* 2353 */ "SELECT_SUFFIX\0"
+/* 2367 */ "select-suffix\0"
+/* 2381 */ "name to add to definition list\0"
+/* 2412 */ "DEFINE\0"
+/* 2419 */ "define\0"
+/* 2426 */ "definition list removal pattern\0"
+/* 2458 */ "UNDEFINE\0"
+/* 2467 */ "undefine\0"
+/* 2476 */ "This option is used to automate dependency tracking:\0"
+/* 2529 */ "emit make dependency file\0"
+/* 2555 */ "MAKE_DEP\0"
+/* 2564 */ "make-dep\0"
+/* 2573 */ "Display extended usage information and exit\0"
+/* 2617 */ "help\0"
+/* 2622 */ "Extended usage information passed thru pager\0"
+/* 2667 */ "more-help\0"
+/* 2677 */ "Output version information and exit\0"
+/* 2713 */ "version\0"
+/* 2721 */ "Reset an option's state\0"
+/* 2745 */ "reset-option\0"
+/* 2758 */ "Abbreviated usage to stdout\0"
+/* 2786 */ "usage\0"
+/* 2792 */ "Save the option state to a config file\0"
+/* 2831 */ "save-opts\0"
+/* 2841 */ "Load options from a config file\0"
+/* 2873 */ "LOAD_OPTS\0"
+/* 2883 */ "no-load-opts\0"
+/* 2896 */ "AUTOGEN\0"
+/* 2904 */ "autogen (GNU AutoGen) - The Automated Program Generator - Ver. 5.16.2\n"
+ "USAGE: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [ <def-file> ]\n\0"
+/* 3047 */ "$HOME\0"
+/* 3053 */ ".\0"
+/* 3055 */ ".autogenrc\0"
+/* 3066 */ "autogen-users@lists.sourceforge.net\0"
+/* 3102 */ "\n"
+ "AutoGen creates text files from templates using external definitions.\n\0"
+/* 3174 */ "\n"
+ "AutoGen is a tool designed for generating program files that contain\n"
+ "repetitive text with varied substitutions.\n\0"
+/* 3288 */ "autogen (GNU AutoGen) 5.16.2\0"
+/* 3317 */ "nothing\0"
+/* 3325 */ "debug-message\0"
+/* 3339 */ "server-shell\0"
+/* 3352 */ "templates\0"
+/* 3362 */ "block-macros\0"
+/* 3375 */ "expressions\0"
+/* 3387 */ "everything";
+
+/*
+ * input-select option description:
+ */
+#define INPUT_SELECT_DESC (autogen_opt_strs+1021)
+#define INPUT_SELECT_FLAGS (OPTST_DOCUMENT | OPTST_NO_INIT)
+
+/*
+ * templ-dirs option description:
+ */
+/* TRANSLATORS: the option argument is a file name */
+#define TEMPL_DIRS_DESC (autogen_opt_strs+1102)
+#define TEMPL_DIRS_NAME (autogen_opt_strs+1133)
+#define TEMPL_DIRS_name (autogen_opt_strs+1144)
+#define TEMPL_DIRS_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * override-tpl option description:
+ */
+#define OVERRIDE_TPL_DESC (autogen_opt_strs+1155)
+#define OVERRIDE_TPL_NAME (autogen_opt_strs+1178)
+#define OVERRIDE_TPL_name (autogen_opt_strs+1191)
+#define OVERRIDE_TPL_FLAGS (OPTST_DISABLED | OPTST_NO_INIT \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * lib-template option description:
+ */
+#define LIB_TEMPLATE_DESC (autogen_opt_strs+1204)
+#define LIB_TEMPLATE_NAME (autogen_opt_strs+1226)
+#define LIB_TEMPLATE_name (autogen_opt_strs+1239)
+#define LIB_TEMPLATE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * definitions option description:
+ */
+#define DEFINITIONS_DESC (autogen_opt_strs+1252)
+#define DEFINITIONS_NAME (autogen_opt_strs+1275)
+#define NOT_DEFINITIONS_name (autogen_opt_strs+1287)
+#define NOT_DEFINITIONS_PFX (autogen_opt_strs+1302)
+#define DEFINITIONS_name (NOT_DEFINITIONS_name + 3)
+#define DEFINITIONS_FLAGS (OPTST_INITENABLED | OPTST_NO_INIT \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * load-scheme option description:
+ */
+#define LOAD_SCHEME_DESC (autogen_opt_strs+1305)
+#define LOAD_SCHEME_NAME (autogen_opt_strs+1330)
+#define LOAD_SCHEME_name (autogen_opt_strs+1342)
+#define LOAD_SCHEME_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * load-functions option description:
+ */
+#ifdef HAVE_DLOPEN
+#define LOAD_FUNCTIONS_DESC (autogen_opt_strs+1354)
+#define LOAD_FUNCTIONS_NAME (autogen_opt_strs+1383)
+#define LOAD_FUNCTIONS_name (autogen_opt_strs+1398)
+#define LOAD_FUNCTIONS_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable load-functions */
+#define LOAD_FUNCTIONS_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define LOAD_FUNCTIONS_NAME NULL
+#define LOAD_FUNCTIONS_DESC NULL
+#define LOAD_FUNCTIONS_name NULL
+#endif /* HAVE_DLOPEN */
+
+/*
+ * shell option description:
+ */
+#ifdef SHELL_ENABLED
+#define SHELL_DESC (autogen_opt_strs+1413)
+#define SHELL_NAME (autogen_opt_strs+1447)
+#define SHELL_name (autogen_opt_strs+1453)
+#define SHELL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable shell */
+#define SHELL_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define SHELL_NAME NULL
+#define SHELL_DESC NULL
+#define SHELL_name NULL
+#endif /* SHELL_ENABLED */
+
+/*
+ * no-fmemopen option description:
+ */
+#define NO_FMEMOPEN_DESC (autogen_opt_strs+1459)
+#define NO_FMEMOPEN_NAME (autogen_opt_strs+1485)
+#define NO_FMEMOPEN_name (autogen_opt_strs+1497)
+#define NO_FMEMOPEN_FLAGS (OPTST_DISABLED)
+
+/*
+ * equate option description:
+ */
+#define EQUATE_DESC (autogen_opt_strs+1509)
+#define EQUATE_NAME (autogen_opt_strs+1542)
+#define EQUATE_name (autogen_opt_strs+1549)
+#define EQUATE_DFT_ARG (autogen_opt_strs+1556)
+#define EQUATE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * out-handling option description:
+ */
+#define OUT_HANDLING_DESC (autogen_opt_strs+1560)
+#define OUT_HANDLING_FLAGS (OPTST_DOCUMENT | OPTST_NO_INIT)
+
+/*
+ * base-name option description:
+ */
+#define BASE_NAME_DESC (autogen_opt_strs+1612)
+#define BASE_NAME_NAME (autogen_opt_strs+1641)
+#define BASE_NAME_name (autogen_opt_strs+1651)
+#define BASE_NAME_FLAGS (OPTST_DISABLED | OPTST_NO_INIT \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * source-time option description:
+ */
+#define SOURCE_TIME_DESC (autogen_opt_strs+1661)
+#define SOURCE_TIME_NAME (autogen_opt_strs+1692)
+#define NOT_SOURCE_TIME_name (autogen_opt_strs+1704)
+#define NOT_SOURCE_TIME_PFX (autogen_opt_strs+1302)
+#define SOURCE_TIME_name (NOT_SOURCE_TIME_name + 3)
+#define SOURCE_TIME_FLAGS (OPTST_DISABLED)
+
+/*
+ * writable option description:
+ */
+#define WRITABLE_DESC (autogen_opt_strs+1719)
+#define WRITABLE_NAME (autogen_opt_strs+1753)
+#define NOT_WRITABLE_name (autogen_opt_strs+1762)
+#define NOT_WRITABLE_PFX (autogen_opt_strs+1775)
+#define WRITABLE_name (NOT_WRITABLE_name + 4)
+#define WRITABLE_FLAGS (OPTST_DISABLED)
+
+/*
+ * debug-tpl option description:
+ */
+#define DEBUG_TPL_DESC (autogen_opt_strs+1779)
+#define DEBUG_TPL_FLAGS (OPTST_DOCUMENT | OPTST_NO_INIT)
+
+/*
+ * loop-limit option description:
+ */
+#define LOOP_LIMIT_DESC (autogen_opt_strs+1849)
+#define LOOP_LIMIT_NAME (autogen_opt_strs+1874)
+#define LOOP_LIMIT_name (autogen_opt_strs+1885)
+#define LOOP_LIMIT_DFT_ARG ((char const*)256)
+#define LOOP_LIMIT_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC) \
+ | OPTST_SCALED_NUM)
+
+/*
+ * timeout option description:
+ */
+#ifdef SHELL_ENABLED
+#define TIMEOUT_DESC (autogen_opt_strs+1896)
+#define TIMEOUT_NAME (autogen_opt_strs+1924)
+#define TIMEOUT_name (autogen_opt_strs+1932)
+#define TIMEOUT_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+#else /* disable timeout */
+#define TIMEOUT_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define TIMEOUT_NAME NULL
+#define TIMEOUT_DESC NULL
+#define TIMEOUT_name NULL
+#endif /* SHELL_ENABLED */
+
+/*
+ * trace option description:
+ */
+#define TRACE_DESC (autogen_opt_strs+1940)
+#define TRACE_NAME (autogen_opt_strs+1964)
+#define TRACE_name (autogen_opt_strs+1970)
+#define TRACE_DFT_ARG ((char const*)TRACE_NOTHING)
+#define TRACE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_ENUMERATION))
+
+/*
+ * trace-out option description:
+ */
+#define TRACE_OUT_DESC (autogen_opt_strs+1976)
+#define TRACE_OUT_NAME (autogen_opt_strs+2006)
+#define TRACE_OUT_name (autogen_opt_strs+2016)
+#define TRACE_OUT_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * show-defs option description:
+ */
+#ifdef DEBUG_ENABLED
+#define SHOW_DEFS_DESC (autogen_opt_strs+2026)
+#define SHOW_DEFS_NAME (autogen_opt_strs+2051)
+#define SHOW_DEFS_name (autogen_opt_strs+2061)
+#define SHOW_DEFS_FLAGS (OPTST_DISABLED | OPTST_NO_INIT)
+
+#else /* disable show-defs */
+#define SHOW_DEFS_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define SHOW_DEFS_NAME NULL
+#define SHOW_DEFS_DESC (NULL)
+#define SHOW_DEFS_name (autogen_opt_strs+2061)
+#endif /* DEBUG_ENABLED */
+
+/*
+ * used-defines option description:
+ */
+#define USED_DEFINES_DESC (autogen_opt_strs+2071)
+#define USED_DEFINES_NAME (autogen_opt_strs+2097)
+#define USED_DEFINES_name (autogen_opt_strs+2110)
+#define USED_DEFINES_FLAGS (OPTST_DISABLED | OPTST_NO_INIT)
+
+/*
+ * core option description:
+ */
+#ifdef HAVE_SYS_RESOURCE_H
+#define CORE_DESC (autogen_opt_strs+2123)
+#define CORE_NAME (autogen_opt_strs+2159)
+#define CORE_name (autogen_opt_strs+2164)
+#define CORE_FLAGS (OPTST_DISABLED)
+
+#else /* disable core */
+#define CORE_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define CORE_NAME NULL
+#define CORE_DESC NULL
+#define CORE_name NULL
+#endif /* HAVE_SYS_RESOURCE_H */
+
+/*
+ * processing option description:
+ */
+#define PROCESSING_DESC (autogen_opt_strs+2169)
+#define PROCESSING_FLAGS (OPTST_DOCUMENT | OPTST_NO_INIT)
+
+/*
+ * skip-suffix option description with
+ * "Must also have options" and "Incompatible options":
+ */
+#define SKIP_SUFFIX_DESC (autogen_opt_strs+2271)
+#define SKIP_SUFFIX_NAME (autogen_opt_strs+2302)
+#define SKIP_SUFFIX_name (autogen_opt_strs+2314)
+static int const aSkip_SuffixCantList[] = {
+ INDEX_OPT_SELECT_SUFFIX, NO_EQUIVALENT };
+#define SKIP_SUFFIX_FLAGS (OPTST_DISABLED | OPTST_STACKED | OPTST_NO_INIT \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * select-suffix option description:
+ */
+#define SELECT_SUFFIX_DESC (autogen_opt_strs+2326)
+#define SELECT_SUFFIX_NAME (autogen_opt_strs+2353)
+#define SELECT_SUFFIX_name (autogen_opt_strs+2367)
+#define SELECT_SUFFIX_FLAGS (OPTST_DISABLED | OPTST_NO_INIT \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * define option description:
+ */
+#define DEFINE_DESC (autogen_opt_strs+2381)
+#define DEFINE_NAME (autogen_opt_strs+2412)
+#define DEFINE_name (autogen_opt_strs+2419)
+#define DEFINE_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * undefine option description:
+ */
+#define UNDEFINE_DESC (autogen_opt_strs+2426)
+#define UNDEFINE_NAME (autogen_opt_strs+2458)
+#define UNDEFINE_name (autogen_opt_strs+2467)
+#define UNDEFINE_FLAGS (OPTST_DISABLED | OPTST_NO_INIT \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * dep-track option description:
+ */
+#define DEP_TRACK_DESC (autogen_opt_strs+2476)
+#define DEP_TRACK_FLAGS (OPTST_DOCUMENT | OPTST_NO_INIT)
+
+/*
+ * make-dep option description:
+ */
+#define MAKE_DEP_DESC (autogen_opt_strs+2529)
+#define MAKE_DEP_NAME (autogen_opt_strs+2555)
+#define MAKE_DEP_name (autogen_opt_strs+2564)
+#define MAKE_DEP_FLAGS (OPTST_DISABLED | OPTST_NO_INIT \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) | OPTST_ARG_OPTIONAL)
+
+/*
+ * Help/More_Help/Version option descriptions:
+ */
+#define HELP_DESC (autogen_opt_strs+2573)
+#define HELP_name (autogen_opt_strs+2617)
+#ifdef HAVE_WORKING_FORK
+#define MORE_HELP_DESC (autogen_opt_strs+2622)
+#define MORE_HELP_name (autogen_opt_strs+2667)
+#define MORE_HELP_FLAGS (OPTST_IMM | OPTST_NO_INIT)
+#else
+#define MORE_HELP_DESC NULL
+#define MORE_HELP_name NULL
+#define MORE_HELP_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#endif
+#ifdef NO_OPTIONAL_OPT_ARGS
+# define VER_FLAGS (OPTST_IMM | OPTST_NO_INIT)
+#else
+# define VER_FLAGS (OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) | \
+ OPTST_ARG_OPTIONAL | OPTST_IMM | OPTST_NO_INIT)
+#endif
+#define VER_DESC (autogen_opt_strs+2677)
+#define VER_name (autogen_opt_strs+2713)
+#define RESET_DESC (autogen_opt_strs+2721)
+#define RESET_name (autogen_opt_strs+2745)
+#define RESET_FLAGS (OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) | OPTST_NO_INIT)
+#define USAGE_DESC (autogen_opt_strs+2758)
+#define USAGE_name (autogen_opt_strs+2786)
+#define SAVE_OPTS_DESC (autogen_opt_strs+2792)
+#define SAVE_OPTS_name (autogen_opt_strs+2831)
+#define LOAD_OPTS_DESC (autogen_opt_strs+2841)
+#define LOAD_OPTS_NAME (autogen_opt_strs+2873)
+#define NO_LOAD_OPTS_name (autogen_opt_strs+2883)
+#define LOAD_OPTS_pfx (autogen_opt_strs+1302)
+#define LOAD_OPTS_name (NO_LOAD_OPTS_name + 3)
+/*
+ * Declare option callback procedures
+ */
+#ifdef HAVE_DLOPEN
+ static tOptProc doOptLoad_Functions;
+#else /* not HAVE_DLOPEN */
+# define doOptLoad_Functions NULL
+#endif /* def/not HAVE_DLOPEN */
+#ifdef SHELL_ENABLED
+ static tOptProc doOptShell;
+#else /* not SHELL_ENABLED */
+# define doOptShell NULL
+#endif /* def/not SHELL_ENABLED */
+#ifdef SHELL_ENABLED
+ static tOptProc doOptTimeout;
+#else /* not SHELL_ENABLED */
+# define doOptTimeout NULL
+#endif /* def/not SHELL_ENABLED */
+extern tOptProc
+ config_dep, optionBooleanVal, optionNestedVal,
+ optionNumericVal, optionPagedUsage, optionPrintVersion,
+ optionResetOpt, optionStackArg, optionTimeDate,
+ optionTimeVal, optionUnstackArg, optionVendorOption;
+static tOptProc
+ doOptLib_Template, doOptLoad_Scheme, doOptLoop_Limit,
+ doOptOverride_Tpl, doOptSelect_Suffix, doOptTrace,
+ doUsageOpt;
+#define VER_PROC optionPrintVersion
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Define the autogen Option Descriptions.
+ * This is an array of OPTION_CT entries, one for each
+ * option that the autogen program responds to.
+ */
+static tOptDesc optDesc[OPTION_CT] = {
+ { /* entry idx, value */ 0, 0,
+ /* equiv idx, value */ 0, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 0, 0,
+ /* opt state flags */ INPUT_SELECT_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ INPUT_SELECT_DESC, NULL, NULL,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 1, VALUE_OPT_TEMPL_DIRS,
+ /* equiv idx, value */ 1, VALUE_OPT_TEMPL_DIRS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ TEMPL_DIRS_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --templ-dirs */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ TEMPL_DIRS_DESC, TEMPL_DIRS_NAME, TEMPL_DIRS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 2, VALUE_OPT_OVERRIDE_TPL,
+ /* equiv idx, value */ 2, VALUE_OPT_OVERRIDE_TPL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OVERRIDE_TPL_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --override-tpl */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptOverride_Tpl,
+ /* desc, NAME, name */ OVERRIDE_TPL_DESC, OVERRIDE_TPL_NAME, OVERRIDE_TPL_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 3, VALUE_OPT_LIB_TEMPLATE,
+ /* equiv idx, value */ 3, VALUE_OPT_LIB_TEMPLATE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ LIB_TEMPLATE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --lib-template */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptLib_Template,
+ /* desc, NAME, name */ LIB_TEMPLATE_DESC, LIB_TEMPLATE_NAME, LIB_TEMPLATE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 4, VALUE_OPT_DEFINITIONS,
+ /* equiv idx, value */ 4, VALUE_OPT_DEFINITIONS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ DEFINITIONS_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --definitions */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ DEFINITIONS_DESC, DEFINITIONS_NAME, DEFINITIONS_name,
+ /* disablement strs */ NOT_DEFINITIONS_name, NOT_DEFINITIONS_PFX },
+
+ { /* entry idx, value */ 5, VALUE_OPT_LOAD_SCHEME,
+ /* equiv idx, value */ 5, VALUE_OPT_LOAD_SCHEME,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ LOAD_SCHEME_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --load-scheme */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptLoad_Scheme,
+ /* desc, NAME, name */ LOAD_SCHEME_DESC, LOAD_SCHEME_NAME, LOAD_SCHEME_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 6, VALUE_OPT_LOAD_FUNCTIONS,
+ /* equiv idx, value */ 6, VALUE_OPT_LOAD_FUNCTIONS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ LOAD_FUNCTIONS_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --load-functions */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptLoad_Functions,
+ /* desc, NAME, name */ LOAD_FUNCTIONS_DESC, LOAD_FUNCTIONS_NAME, LOAD_FUNCTIONS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 7, VALUE_OPT_SHELL,
+ /* equiv idx, value */ 7, VALUE_OPT_SHELL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SHELL_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --shell */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptShell,
+ /* desc, NAME, name */ SHELL_DESC, SHELL_NAME, SHELL_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 8, VALUE_OPT_NO_FMEMOPEN,
+ /* equiv idx, value */ 8, VALUE_OPT_NO_FMEMOPEN,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NO_FMEMOPEN_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --no-fmemopen */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ NO_FMEMOPEN_DESC, NO_FMEMOPEN_NAME, NO_FMEMOPEN_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 9, VALUE_OPT_EQUATE,
+ /* equiv idx, value */ 9, VALUE_OPT_EQUATE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ EQUATE_FLAGS, 0,
+ /* last opt argumnt */ { EQUATE_DFT_ARG },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ EQUATE_DESC, EQUATE_NAME, EQUATE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 0, 0,
+ /* equiv idx, value */ 0, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 0, 0,
+ /* opt state flags */ OUT_HANDLING_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ OUT_HANDLING_DESC, NULL, NULL,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 11, VALUE_OPT_BASE_NAME,
+ /* equiv idx, value */ 11, VALUE_OPT_BASE_NAME,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ BASE_NAME_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --base-name */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ BASE_NAME_DESC, BASE_NAME_NAME, BASE_NAME_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 12, VALUE_OPT_SOURCE_TIME,
+ /* equiv idx, value */ 12, VALUE_OPT_SOURCE_TIME,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SOURCE_TIME_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --source-time */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ SOURCE_TIME_DESC, SOURCE_TIME_NAME, SOURCE_TIME_name,
+ /* disablement strs */ NOT_SOURCE_TIME_name, NOT_SOURCE_TIME_PFX },
+
+ { /* entry idx, value */ 13, VALUE_OPT_WRITABLE,
+ /* equiv idx, value */ 13, VALUE_OPT_WRITABLE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ WRITABLE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --writable */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ WRITABLE_DESC, WRITABLE_NAME, WRITABLE_name,
+ /* disablement strs */ NOT_WRITABLE_name, NOT_WRITABLE_PFX },
+
+ { /* entry idx, value */ 0, 0,
+ /* equiv idx, value */ 0, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 0, 0,
+ /* opt state flags */ DEBUG_TPL_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ DEBUG_TPL_DESC, NULL, NULL,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 15, VALUE_OPT_LOOP_LIMIT,
+ /* equiv idx, value */ 15, VALUE_OPT_LOOP_LIMIT,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ LOOP_LIMIT_FLAGS, 0,
+ /* last opt argumnt */ { LOOP_LIMIT_DFT_ARG },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptLoop_Limit,
+ /* desc, NAME, name */ LOOP_LIMIT_DESC, LOOP_LIMIT_NAME, LOOP_LIMIT_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 16, VALUE_OPT_TIMEOUT,
+ /* equiv idx, value */ 16, VALUE_OPT_TIMEOUT,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ TIMEOUT_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --timeout */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptTimeout,
+ /* desc, NAME, name */ TIMEOUT_DESC, TIMEOUT_NAME, TIMEOUT_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 17, VALUE_OPT_TRACE,
+ /* equiv idx, value */ 17, VALUE_OPT_TRACE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ TRACE_FLAGS, 0,
+ /* last opt argumnt */ { TRACE_DFT_ARG },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptTrace,
+ /* desc, NAME, name */ TRACE_DESC, TRACE_NAME, TRACE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 18, VALUE_OPT_TRACE_OUT,
+ /* equiv idx, value */ 18, VALUE_OPT_TRACE_OUT,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ TRACE_OUT_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --trace-out */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ TRACE_OUT_DESC, TRACE_OUT_NAME, TRACE_OUT_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 19, VALUE_OPT_SHOW_DEFS,
+ /* equiv idx, value */ 19, VALUE_OPT_SHOW_DEFS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SHOW_DEFS_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --show-defs */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ SHOW_DEFS_DESC, SHOW_DEFS_NAME, SHOW_DEFS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 20, VALUE_OPT_USED_DEFINES,
+ /* equiv idx, value */ 20, VALUE_OPT_USED_DEFINES,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ USED_DEFINES_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --used-defines */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ USED_DEFINES_DESC, USED_DEFINES_NAME, USED_DEFINES_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 21, VALUE_OPT_CORE,
+ /* equiv idx, value */ 21, VALUE_OPT_CORE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ CORE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --core */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ CORE_DESC, CORE_NAME, CORE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 0, 0,
+ /* equiv idx, value */ 0, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 0, 0,
+ /* opt state flags */ PROCESSING_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ PROCESSING_DESC, NULL, NULL,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 23, VALUE_OPT_SKIP_SUFFIX,
+ /* equiv idx, value */ 23, VALUE_OPT_SKIP_SUFFIX,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ SKIP_SUFFIX_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --skip-suffix */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aSkip_SuffixCantList,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ SKIP_SUFFIX_DESC, SKIP_SUFFIX_NAME, SKIP_SUFFIX_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 24, VALUE_OPT_SELECT_SUFFIX,
+ /* equiv idx, value */ 24, VALUE_OPT_SELECT_SUFFIX,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ SELECT_SUFFIX_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --select-suffix */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptSelect_Suffix,
+ /* desc, NAME, name */ SELECT_SUFFIX_DESC, SELECT_SUFFIX_NAME, SELECT_SUFFIX_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 25, VALUE_OPT_DEFINE,
+ /* equiv idx, value */ 25, VALUE_OPT_DEFINE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DEFINE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --define */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ DEFINE_DESC, DEFINE_NAME, DEFINE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 26, VALUE_OPT_UNDEFINE,
+ /* equiv idx, value */ NOLIMIT, NOLIMIT,
+ /* equivalenced to */ INDEX_OPT_DEFINE,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ UNDEFINE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --undefine */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionUnstackArg,
+ /* desc, NAME, name */ UNDEFINE_DESC, UNDEFINE_NAME, UNDEFINE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 0, 0,
+ /* equiv idx, value */ 0, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 0, 0,
+ /* opt state flags */ DEP_TRACK_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ DEP_TRACK_DESC, NULL, NULL,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 28, VALUE_OPT_MAKE_DEP,
+ /* equiv idx, value */ 28, VALUE_OPT_MAKE_DEP,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ MAKE_DEP_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --make-dep */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ config_dep,
+ /* desc, NAME, name */ MAKE_DEP_DESC, MAKE_DEP_NAME, MAKE_DEP_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_RESET_OPTION, VALUE_OPT_RESET_OPTION,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_RESET_OPTION,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ RESET_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionResetOpt,
+ /* desc, NAME, name */ RESET_DESC, NULL, RESET_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_VERSION, VALUE_OPT_VERSION,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_VERSION,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ VER_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ VER_PROC,
+ /* desc, NAME, name */ VER_DESC, NULL, VER_name,
+ /* disablement strs */ NULL, NULL },
+
+
+
+ { /* entry idx, value */ INDEX_OPT_HELP, VALUE_OPT_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_HELP,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doUsageOpt,
+ /* desc, NAME, name */ HELP_DESC, NULL, HELP_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_MORE_HELP, VALUE_OPT_MORE_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_MORE_HELP,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ MORE_HELP_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionPagedUsage,
+ /* desc, NAME, name */ MORE_HELP_DESC, NULL, MORE_HELP_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_USAGE, VALUE_OPT_USAGE,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_USAGE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doUsageOpt,
+ /* desc, NAME, name */ USAGE_DESC, NULL, USAGE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_SAVE_OPTS, VALUE_OPT_SAVE_OPTS,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_SAVE_OPTS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_SET_ARGTYPE(OPARG_TYPE_STRING)
+ | OPTST_ARG_OPTIONAL | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ SAVE_OPTS_DESC, NULL, SAVE_OPTS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_LOAD_OPTS, VALUE_OPT_LOAD_OPTS,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_LOAD_OPTS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ OPTST_SET_ARGTYPE(OPARG_TYPE_STRING)
+ | OPTST_DISABLE_IMM, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionLoadOpt,
+ /* desc, NAME, name */ LOAD_OPTS_DESC, LOAD_OPTS_NAME, LOAD_OPTS_name,
+ /* disablement strs */ NO_LOAD_OPTS_name, LOAD_OPTS_pfx }
+};
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Define the autogen Option Environment
+ */
+#define zPROGNAME (autogen_opt_strs+2896)
+#define zUsageTitle (autogen_opt_strs+2904)
+#define zRcName (autogen_opt_strs+3055)
+static char const * const apzHomeList[3] = {
+ autogen_opt_strs+3047,
+ autogen_opt_strs+3053,
+ NULL };
+#define zBugsAddr (autogen_opt_strs+3066)
+#define zExplain (autogen_opt_strs+3102)
+#define zDetail (autogen_opt_strs+3174)
+#define zFullVersion (autogen_opt_strs+3288)
+/* extracted from optcode.tlib near line 350 */
+
+#define OPTPROC_BASE OPTPROC_NONE
+#define translate_option_strings NULL
+
+static optArgBucket_t const original_autogen_defaults[ OPTION_CT ] = {
+ { NULL }, /* doc opt */
+ { NULL }, /* --templ-dirs */
+ { NULL }, /* --override-tpl */
+ { NULL }, /* --lib-template */
+ { NULL }, /* --definitions */
+ { NULL }, /* --load-scheme */
+ { NULL }, /* --load-functions */
+ { NULL }, /* --shell */
+ { NULL }, /* --no-fmemopen */
+ { EQUATE_DFT_ARG },
+ { NULL }, /* doc opt */
+ { NULL }, /* --base-name */
+ { NULL }, /* --source-time */
+ { NULL }, /* --writable */
+ { NULL }, /* doc opt */
+ { LOOP_LIMIT_DFT_ARG },
+ { NULL }, /* --timeout */
+ { TRACE_DFT_ARG },
+ { NULL }, /* --trace-out */
+ { NULL }, /* --show-defs */
+ { NULL }, /* --used-defines */
+ { NULL }, /* --core */
+ { NULL }, /* doc opt */
+ { NULL }, /* --skip-suffix */
+ { NULL }, /* --select-suffix */
+ { NULL }, /* --define */
+ { NULL }, /* --undefine */
+ { NULL }, /* doc opt */
+ { NULL }, /* --make-dep */
+ { NULL }, /* resettable */
+ { NULL }, /* version */
+ { NULL }, /* help */
+ { NULL }, /* more-help */
+ { NULL }, /* usage-opt */
+ { NULL }, /* save-opts */
+ { NULL } /* load-opts */
+};
+static void * const original_autogen_cookies[ OPTION_CT ] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+
+#define autogen_full_usage (NULL)
+
+#define autogen_short_usage (NULL)
+
+#endif /* not defined __doxygen__ */
+
+/*
+ * Create the static procedure(s) declared above.
+ */
+/**
+ * The callout function that invokes the optionUsage function.
+ *
+ * @param pOptions the AutoOpts option description structure
+ * @param pOptDesc the descriptor for the "help" (usage) option.
+ * @noreturn
+ */
+static void
+doUsageOpt(tOptions * pOptions, tOptDesc * pOptDesc)
+{
+ if ((pOptDesc->fOptState & OPTST_RESET) != 0)
+ return;
+
+ int ex_code = (pOptDesc->optIndex == INDEX_OPT_HELP)
+ ? AUTOGEN_EXIT_SUCCESS : AO_EXIT_REQ_USAGE;
+ optionUsage(&autogenOptions, ex_code);
+ /* NOTREACHED */
+ (void)pOptions;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the override-tpl option.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+static void
+doOptOverride_Tpl(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /* extracted from opts.def, line 171 */
+ tpl_fname = pOptDesc->optArg.argString;
+ (void)pOptions;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the lib-template option.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+static void
+doOptLib_Template(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /* extracted from opts.def, line 192 */
+ templ_t* pT;
+ processing_state = PROC_STATE_LIB_LOAD;
+ pT = tpl_load(pOptDesc->optArg.argString, NULL);
+ tpl_unload(pT);
+ processing_state = PROC_STATE_OPTIONS;
+ (void)pOptions;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the load-scheme option.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+static void
+doOptLoad_Scheme(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /* extracted from opts.def, line 254 */
+ proc_state_t saveState = processing_state;
+ char* pz;
+ char zPath[MAXPATHLEN];
+ static char const * const apzSfx[] = { "scm", NULL };
+
+ if (! SUCCESSFUL(
+ find_file(pOptDesc->optArg.argString, zPath, apzSfx, NULL))) {
+
+ usage_message(CANNOT_LOCATE_FMT,
+ pOptDesc->optArg.argString);
+ /* NOTREACHED */
+ }
+
+ pz = aprf(LOAD_GUILE_FILE_FMT, zPath);
+ processing_state = PROC_STATE_GUILE_PRELOAD;
+ (void)ag_scm_c_eval_string_from_file_line(
+ pz, __FILE__, __LINE__);
+ free(pz);
+ processing_state = saveState;
+ (void)pOptions;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the load-functions option, when HAVE_DLOPEN is #define-d.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+#ifdef HAVE_DLOPEN
+static void
+doOptLoad_Functions(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /* extracted from opts.def, line 304 */
+ void* hdl = dlopen(
+ pOptDesc->optArg.argString, RTLD_NOW|RTLD_GLOBAL);
+ init_proc_t* proc;
+
+ if (hdl == NULL) {
+ char const* pzErr = dlerror();
+ fprintf(stderr, DLOPEN_ERROR_FMT,
+ pOptDesc->optArg.argString, errno, pzErr);
+ exit(EXIT_FAILURE);
+ }
+ proc = (init_proc_t*)dlsym(hdl, "scm_init");
+ if (proc == NULL) {
+ fprintf(stderr, SYM_NOT_FOUND_FMT,
+ pOptDesc->optArg.argString);
+ }
+ else (*proc)();
+ (void)pOptions;
+}
+#endif /* defined HAVE_DLOPEN */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the shell option, when SHELL_ENABLED is #define-d.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+#ifdef SHELL_ENABLED
+static void
+doOptShell(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /* extracted from opts.def, line 341 */
+ shell_program = pOptDesc->optArg.argString;
+ (void)pOptions;
+}
+#endif /* defined SHELL_ENABLED */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the loop-limit option.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+static void
+doOptLoop_Limit(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ static struct {long rmin, rmax;} const rng[2] = {
+ { -1, LONG_MIN }, { 1, 0x1000000 } };
+ int ix;
+
+ if (pOptions <= OPTPROC_EMIT_LIMIT)
+ goto emit_ranges;
+ if ((pOptDesc->fOptState & OPTST_RESET) != 0)
+ return;
+ optionNumericVal(pOptions, pOptDesc);
+
+ for (ix = 0; ix < 2; ix++) {
+ if (pOptDesc->optArg.argInt < rng[ix].rmin)
+ continue; /* ranges need not be ordered. */
+ if (pOptDesc->optArg.argInt == rng[ix].rmin)
+ return;
+ if (rng[ix].rmax == LONG_MIN)
+ continue;
+ if (pOptDesc->optArg.argInt <= rng[ix].rmax)
+ return;
+ }
+
+ option_usage_fp = stderr;
+
+emit_ranges:
+ optionShowRange(pOptions, pOptDesc, (void *)rng, 2);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the timeout option, when SHELL_ENABLED is #define-d.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+#ifdef SHELL_ENABLED
+static void
+doOptTimeout(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ static struct {long rmin, rmax;} const rng[1] = {
+ { 0, 3600 } };
+ int ix;
+
+ if (pOptions <= OPTPROC_EMIT_LIMIT)
+ goto emit_ranges;
+ if ((pOptDesc->fOptState & OPTST_RESET) != 0)
+ return;
+ optionNumericVal(pOptions, pOptDesc);
+
+ for (ix = 0; ix < 1; ix++) {
+ if (pOptDesc->optArg.argInt < rng[ix].rmin)
+ continue; /* ranges need not be ordered. */
+ if (pOptDesc->optArg.argInt == rng[ix].rmin)
+ return;
+ if (rng[ix].rmax == LONG_MIN)
+ continue;
+ if (pOptDesc->optArg.argInt <= rng[ix].rmax)
+ return;
+ }
+
+ option_usage_fp = stderr;
+
+emit_ranges:
+ optionShowRange(pOptions, pOptDesc, (void *)rng, 1);
+}
+#endif /* defined SHELL_ENABLED */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the trace option.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+static void
+doOptTrace(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+
+/* extracted from optmain.tlib near line 871 */
+ static char const * const azNames[7] = {
+ autogen_opt_strs+3317, autogen_opt_strs+3325, autogen_opt_strs+3339,
+ autogen_opt_strs+3352, autogen_opt_strs+3362, autogen_opt_strs+3375,
+ autogen_opt_strs+3387 };
+
+ if (pOptions <= OPTPROC_EMIT_LIMIT) {
+ (void) optionEnumerationVal(pOptions, pOptDesc, azNames, 7);
+ return; /* protect AutoOpts client code from internal callbacks */
+ }
+ if ((pOptDesc->fOptState & OPTST_RESET) != 0)
+ return;
+
+ pOptDesc->optArg.argEnum =
+ optionEnumerationVal(pOptions, pOptDesc, azNames, 7);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the select-suffix option.
+ *
+ * @param pOptions the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+static void
+doOptSelect_Suffix(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /* extracted from opts.def, line 781 */
+ char const * arg = pOptDesc->optArg.argString;
+ if ((arg != NULL) && (*arg != NUL))
+ (void)do_suffix(arg, NULL, -1);
+ (void)pOptions;
+}
+/* extracted from optmain.tlib near line 1146 */
+
+/**
+ * Print a usage message with a format and va_list argument.
+ * The optionUsage function is then invoked to print
+ * the error usage text (somewhat abbreviated) and then exit.
+ *
+ * @param[in] fmt the message format string
+ * @param[in] ap the var-arg list.
+ * @noreturn
+ */
+void
+vusage_message(char const * fmt, va_list ap)
+{
+ char const * er_leader = _("autogen usage error:\n");
+ fputs(er_leader, stderr);
+ vfprintf(stderr, fmt, ap);
+ optionUsage(&autogenOptions, AUTOGEN_EXIT_OPTION_ERROR);
+ /* NOTREACHED */
+}
+
+/**
+ * Print a usage message with a format and a variable argument list.
+ * vusage_message() is called to do the work.
+ *
+ * @param[in] fmt the message format string
+ * @param[in] ... the argument list for the message
+ * @noreturn
+ */
+void
+usage_message(char const * fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vusage_message(fmt, ap);
+ /* NOTREACHED */
+ va_end(ap);
+}
+
+/**
+ * The directory containing the data associated with autogen.
+ */
+#ifndef PKGDATADIR
+# define PKGDATADIR ""
+#endif
+
+/**
+ * Information about the person or institution that packaged autogen
+ * for the current distribution.
+ */
+#ifndef WITH_PACKAGER
+# define autogen_packager_info NULL
+#else
+static char const autogen_packager_info[] =
+ "Packaged by " WITH_PACKAGER
+
+# ifdef WITH_PACKAGER_VERSION
+ " ("WITH_PACKAGER_VERSION")"
+# endif
+
+# ifdef WITH_PACKAGER_BUG_REPORTS
+ "\nReport autogen bugs to " WITH_PACKAGER_BUG_REPORTS
+# endif
+ "\n";
+#endif
+#ifndef __doxygen__
+
+#endif /* __doxygen__ */
+/**
+ * The option definitions for autogen. The one structure that
+ * binds them all.
+ */
+tOptions autogenOptions = {
+ OPTIONS_STRUCT_VERSION,
+ 0, NULL, /* original argc + argv */
+ ( OPTPROC_BASE
+ + OPTPROC_ERRSTOP
+ + OPTPROC_SHORTOPT
+ + OPTPROC_LONGOPT
+ + OPTPROC_NO_REQ_OPT
+ + OPTPROC_NEGATIONS
+ + OPTPROC_ENVIRON ),
+ 0, NULL, /* current option index, current option */
+ NULL, NULL, zPROGNAME,
+ zRcName, zCopyright, zLicenseDescrip,
+ zFullVersion, apzHomeList, zUsageTitle,
+ zExplain, zDetail, optDesc,
+ zBugsAddr, /* address to send bugs to */
+ NULL, NULL, /* extensions/saved state */
+ optionUsage, /* usage procedure */
+ translate_option_strings, /* translation procedure */
+ /*
+ * Indexes to special options
+ */
+ { INDEX_OPT_MORE_HELP, /* more-help option index */
+ INDEX_OPT_SAVE_OPTS, /* save option index */
+ NO_EQUIVALENT, /* '-#' option index */
+ NO_EQUIVALENT /* index of default opt */
+ },
+ 36 /* full option count */, 29 /* user option count */,
+ autogen_full_usage, autogen_short_usage,
+ original_autogen_defaults, original_autogen_cookies,
+ PKGDATADIR, autogen_packager_info
+};
+
+#ifdef __cplusplus
+}
+#endif
+/* opts.c ends here */
diff --git a/agen5/opts.def b/agen5/opts.def
new file mode 100644
index 0000000..c595909
--- /dev/null
+++ b/agen5/opts.def
@@ -0,0 +1,998 @@
+/* -*- Mode: conf -*- */
+
+autogen definitions options;
+
+/*
+ * Time-stamp: "2012-04-15 11:12:30 bkorb"
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+copyright = {
+ date = "1992-2012";
+ owner = "Bruce Korb";
+ eaddr = 'autogen-users@lists.sourceforge.net';
+ type = gpl;
+};
+
+/*
+ * "XML2AG" is a wrapper around AutoGen. Therefore, this file serves
+ * to describe the options for both programs. Which is being described
+ * depends on the definition state of "XML2AG".
+ */
+#ifndef XML2AG
+prog-name = "autogen";
+prog-title = "The Automated Program Generator";
+homerc = $HOME, ".";
+environrc;
+usage-opt;
+usage-message;
+resettable;
+config-header = config.h;
+no-xlate = anything;
+
+#else
+prog-name = xml2ag;
+prog-title = "XML to AutoGen Definiton Converter";
+#endif
+package = 'GNU AutoGen';
+
+argument = "[ <def-file> ]";
+long-opts;
+
+version = `
+ if test ! -d "${top_srcdir}"; then
+ echo "NOTICE: Setting top_srcdir to .." >&2
+ top_srcdir=..
+ fi
+ test -f ${top_srcdir}/VERSION || \
+ die "error ${top_srcdir}/VERSION file missing"
+ eval \`egrep '^AG_[A-Z_]*=' ${top_srcdir}/VERSION\` 2> /dev/null
+ echo $AG_VERSION `;
+
+include = "[= AutoGen5 Template =]";
+#ifndef XML2AG
+include = <<- _END_INCLUDE
+ #include "autogen.h"
+ #ifdef HAVE_DLOPEN
+ # ifdef HAVE_DLFCN_H
+ # include <dlfcn.h>
+ # else
+ extern void* dlopen(char const*,int);
+ # endif
+
+ # ifndef RTLD_GLOBAL
+ # define RTLD_GLOBAL 0
+ # endif
+
+ # ifndef RTLD_NOW
+ # ifdef RTLD_LAZY
+ # define RTLD_NOW DL_LAZY
+ # else
+ # define RTLD_NOW 0
+ # endif
+ # endif
+ #endif
+
+ #if HAVE_CTYPE_H
+ # include <ctype.h>
+ #else
+ # define isspace(_c) 0
+ #endif
+
+ typedef void (init_proc_t)(void);
+ char const * tpl_fname = NULL;
+ bool trace_is_to_pipe = false;
+ _END_INCLUDE;
+
+export = <<- _EOExport_
+ extern char const * tpl_fname;
+ extern bool trace_is_to_pipe;
+ _EOExport_;
+#endif /* XML2AG */
+
+exit-name[1] = option-error;
+exit-desc[1] = 'The command options were misconfigured.';
+exit-name[2] = bad_template;
+exit-desc[2] = 'An error was encountered processing the template.';
+exit-name[3] = bad_definitions;
+exit-desc[3] = 'The definitions could not be deciphered.';
+exit-name[4] = load-error;
+exit-desc[4] = 'An error was encountered during the load phase.';
+exit-name[5] = signal;
+exit-desc[5] = <<- _EndDesc_
+ Program exited due to catching a signal. If your template includes
+ string formatting, a number argument to a "%s" formatting element will
+ trigger a segmentation fault. Autogen will catch the seg fault signal
+ and exit with @code{AUTOGEN_EXIT_SIGNAL(5)}. Alternatively, AutoGen
+ may have been interrupted with a @code{kill(2)} signal.
+ _EndDesc_;
+
+#ifndef XML2AG
+flag = {
+ name = input-select;
+ documentation;
+
+ descrip =
+'The following options select definitions, templates and '
+'scheme functions to use';
+};
+#endif
+
+flag = {
+ name = templ-dirs;
+ value = L;
+ arg-type = string;
+ descrip = "Template search directory list";
+ max = NOLIMIT;
+ arg-name = dir;
+ translators = 'the option argument is a file name';
+ stack-arg;
+#ifndef XML2AG
+ settable;
+ doc = <<- _EOF_
+ Add a directory to the list of directories to search when opening
+ a template, either as the primary template or an included one.
+ The last entry has the highest priority in the search list.
+ That is to say, they are searched in reverse order.
+ _EOF_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = override-tpl;
+ value = T;
+ arg-type = string;
+ arg-name = tpl-file;
+ descrip = "Override template file";
+#ifndef XML2AG
+ no-preset;
+ doc = <<- _EOF_
+ Definition files specify the standard template that is to be expanded.
+ This option will override that name and expand a different template.
+ _EOF_;
+
+ flag-code = " tpl_fname = pOptDesc->optArg.argString;";
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = lib-template;
+ value = l;
+ arg-type = string;
+ arg-name = tpl-file;
+ descrip = "Library template file";
+ max = NOLIMIT;
+#ifndef XML2AG
+ doc = <<- _EOF_
+ DEFINE macros are saved from this template file for use in processing
+ the main macro file. Template text aside from the DEFINE macros is
+ is ignored.
+ _EOF_;
+
+ flag-code = <<- _EOCode_
+ templ_t* pT;
+ processing_state = PROC_STATE_LIB_LOAD;
+ pT = tpl_load(pOptDesc->optArg.argString, NULL);
+ tpl_unload(pT);
+ processing_state = PROC_STATE_OPTIONS;
+ _EOCode_;
+#else
+ stack-arg;
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = 'definitions';
+ arg-type = string;
+ arg-name = file;
+ descrip = "Definitions input file";
+#ifndef XML2AG
+ disable = no;
+ no-preset;
+ enabled;
+ settable;
+ doc = <<- _EOF_
+ Use this argument to specify the input definitions file with a
+ command line option. If you do not specify this option, then
+ there must be a command line argument that specifies the file,
+ even if only to specify stdin with a hyphen (@code{-}).
+ Specify, @code{--no-definitions} when you wish to process
+ a template without any active AutoGen definitions.
+ _EOF_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+include =
+ '#define CANNOT_LOCATE_FMT ([=
+ (string-table-add-ref opt-strs
+ "Cannot locate scheme file \'%s\'\n")=])' "\n"
+
+ '#define LOAD_GUILE_FILE_FMT ([=
+ (string-table-add-ref opt-strs
+ "(load \"%s\")")=])';
+
+flag = {
+ name = load-scheme;
+ value = S;
+ arg-type = string;
+ arg-name = file;
+ descrip = "Scheme code file to load";
+#ifndef XML2AG
+ settable;
+ doc = <<- _EOF_
+ Use this option to pre-load Scheme scripts into the Guile
+ interpreter before template processing begins.
+ Please note that the AutoGen specific functions are not loaded
+ until after argument processing. So, though they may be specified
+ in lambda functions you define, they may not be invoked until after
+ option processing is complete.
+ _EOF_;
+
+ flag-code = <<- _EOCode_
+ proc_state_t saveState = processing_state;
+ char* pz;
+ char zPath[MAXPATHLEN];
+ static char const * const apzSfx[] = { "scm", NULL };
+
+ if (! SUCCESSFUL(
+ find_file(pOptDesc->optArg.argString, zPath, apzSfx, NULL))) {
+
+ usage_message(CANNOT_LOCATE_FMT,
+ pOptDesc->optArg.argString);
+ /* NOTREACHED */
+ }
+
+ pz = aprf(LOAD_GUILE_FILE_FMT, zPath);
+ processing_state = PROC_STATE_GUILE_PRELOAD;
+ (void)ag_scm_c_eval_string_from_file_line(
+ pz, __FILE__, __LINE__);
+ free(pz);
+ processing_state = saveState;
+ _EOCode_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+include =
+ '#define DLOPEN_ERROR_FMT ([=
+ (string-table-add-ref opt-strs
+ "dlopen(%s) error %d\n%s\n")=])' "\n"
+
+ '#define SYM_NOT_FOUND_FMT ([=
+ (string-table-add-ref opt-strs (string-append
+ "dlsym(scm_init) not found in %s\n"
+ "\tyou must initialize the library yourself\n")) =])';
+
+flag = {
+ name = load-functions;
+ value = F;
+ arg-type = string;
+ arg-name = file;
+ descrip = "Load scheme function library";
+ ifdef = HAVE_DLOPEN;
+#ifndef XML2AG
+ doc = <<- _EOF_
+ This option is used to load Guile-scheme functions. The automatically
+ called initialization routine @code{scm_init} must be used to register
+ these routines or data.
+ _EOF_;
+
+ flag-code = <<- _EOCode_
+ void* hdl = dlopen(
+ pOptDesc->optArg.argString, RTLD_NOW|RTLD_GLOBAL);
+ init_proc_t* proc;
+
+ if (hdl == NULL) {
+ char const* pzErr = dlerror();
+ fprintf(stderr, DLOPEN_ERROR_FMT,
+ pOptDesc->optArg.argString, errno, pzErr);
+ exit(EXIT_FAILURE);
+ }
+ proc = (init_proc_t*)dlsym(hdl, "scm_init");
+ if (proc == NULL) {
+ fprintf(stderr, SYM_NOT_FOUND_FMT,
+ pOptDesc->optArg.argString);
+ }
+ else (*proc)();
+ _EOCode_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = shell;
+ arg-type = string;
+ arg-name = shell;
+
+ descrip = "name or path name of shell to use";
+#ifndef XML2AG
+ ifdef = SHELL_ENABLED;
+ doc = <<- _EOF_
+ By default, when AutoGen is built, the configuration is probed for a
+ reasonable Bourne-like shell to use for shell script processing. If
+ a particular template needs an alternate shell, it must be specified
+ with this option on the command line, with an environment variable
+ (@code{SHELL}) or in the configuration/initialization file.
+ _EOF_;
+ flag-code = " shell_program = pOptDesc->optArg.argString;";
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = no-fmemopen;
+ descrip = "Do not use in-mem streams";
+ value = m;
+#ifndef XML2AG
+ doc = <<- _EOF_
+ If the local C library supports "@code{fopencookie(3GNU)}", or
+ "@code{funopen(3BSD)}" then AutoGen prefers to use in-memory stream
+ buffer opens instead of anonymous files. This may lead to problems
+ if there is a shortage of virtual memory. If, for a particular
+ application, you run out of memory, then specify this option.
+ This is unlikely in a modern 64-bit virtual memory environment.
+
+ On platforms without these functions, the option is accepted
+ but ignored. @code{fmemopen(POSIX)} is not adequate because
+ its string buffer is not reallocatable. @code{open_memstream(POSIX)}
+ is @i{also} not adequate because the stream is only opened for
+ output. AutoGen needs a reallocatable buffer available for both
+ reading and writing.
+ _EOF_;
+
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = equate;
+ arg-name = char-list;
+ arg-type = string;
+ descrip = "characters considered equivalent";
+#ifndef XML2AG
+ arg-default = "_-^" /* default equivalence */;
+ doc = <<- _EODoc_
+ This option will alter the list of characters considered equivalent.
+ The default are the three characters, "_-^". (The last is conventional
+ on a Tandem/HP-NonStop, and I used to do a lot of work on Tandems.)
+ _EODoc_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+#ifndef XML2AG
+flag = {
+ name = out-handling;
+ documentation;
+ descrip = 'The following options modify how output is handled';
+};
+#endif
+
+flag = {
+ name = base-name;
+ value = b;
+ arg-type = string;
+ arg-name = name;
+ descrip = "Base name for output file(s)";
+#ifndef XML2AG
+ no-preset;
+ doc = <<- _EOF_
+ A template may specify the exact name of the output file. Normally,
+ it does not. Instead, the name is composed of the base name of the
+ definitions file with suffixes appended. This option will override the
+ base name derived from the definitions file name. This is required if
+ there is no definitions file and advisable if definitions are being
+ read from stdin. If the definitions are being read from standard in,
+ the base name defaults to @file{stdin}. Any leading directory components
+ in the name will be silently removed. If you wish the output file to
+ appear in a particular directory, it is recommended that you "cd" into
+ that directory first, or use directory names in the format specification
+ for the output suffix lists, @xref{pseudo macro}.
+ _EOF_;
+#else
+ settable;
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = source-time;
+ descrip = "set mod times to latest source";
+#ifndef XML2AG
+ disable = no;
+ doc = <<- _EOF_
+ If you stamp your output files with the @code{DNE} macro output, then
+ your output files will always be different, even if the content has
+ not really changed. If you use this option, then the modification
+ time of the output files will change only if the input files change.
+ This will help reduce unneeded builds.
+ _EOF_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = writable;
+ descrip = "Allow output files to be writable";
+ disable = not;
+#ifndef XML2AG
+ settable;
+ doc = <<- _EODoc_
+ This option will leave output files writable.
+ Normally, output files are read-only.
+ _EODoc_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+#ifndef XML2AG
+flag = {
+ name = debug-tpl;
+ documentation = <<- _EODoc_
+ They specify limits that prevent the template from taking overly long
+ or producing more output than expected.
+ _EODoc_;
+ descrip =
+'The following options are often useful while debugging new templates';
+};
+#endif
+
+flag = {
+ name = loop-limit;
+ arg-type = number;
+ arg-default = 256;
+ arg-range = "-1";
+ arg-range = "1->0x1000000"; /* 16 million */
+ scaled;
+
+ arg-name = lim;
+ descrip = "Limit on increment loops";
+#ifndef XML2AG
+ doc = <<- _EODoc_
+ This option prevents runaway loops. For example, if you accidentally
+ specify, "FOR x (for-from 1) (for-to -1) (for-by 1)", it will take a
+ long time to finish. If you do have more than 256 entries in tables,
+ you will need to specify a new limit with this option.
+ _EODoc_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = timeout;
+ value = t;
+ arg-type = number;
+ arg-range = "0->3600"; /* one hour limit */
+ arg-name = time-lim;
+
+ descrip = "Time limit for server shell";
+#ifndef XML2AG
+ ifdef = SHELL_ENABLED;
+ doc = <<- _EOF_
+ AutoGen works with a shell server process. Most normal commands will
+ complete in less than 10 seconds. If, however, your commands need more
+ time than this, use this option.
+
+ The valid range is 0 to 3600 seconds (1 hour).
+ Zero will disable the server time limit.
+ _EOF_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = trace;
+ arg-type = keyword;
+ arg-default = nothing;
+ arg-name = level;
+ descrip = "tracing level of detail";
+ keyword = nothing, debug-message, server-shell, templates,
+ block-macros, expressions, everything;
+
+#ifndef XML2AG
+ doc = <<- _EOF_
+ This option will cause AutoGen to display a trace of its template
+ processing. There are six levels, each level including messages from
+ the previous levels:
+
+ @table @samp
+ @item nothing
+ Does no tracing at all (default)
+
+ @item debug-message
+ Print messages from the "DEBUG" AutoGen macro (@pxref{DEBUG}).
+
+ @item server-shell
+ Traces all input and output to the server shell. This includes a shell
+ "independent" initialization script about 30 lines long. Its output is
+ discarded and not inserted into any template.
+
+ @item templates
+ Traces the invocation of @code{DEFINE}d macros and @code{INCLUDE}s
+
+ @item block-macros
+ Traces all block macros. The above, plus @code{IF}, @code{FOR},
+ @code{CASE} and @code{WHILE}.
+
+ @item expressions
+ Displays the results of expression evaluations.
+
+ @item everything
+ Displays the invocation of every AutoGen macro, even @code{TEXT} macros
+ (i.e. the text outside of macro quotes). Additionally, if you rebuild
+ the ``expr.ini'' file with debugging enabled, then all calls to
+ AutoGen defined scheme functions will also get logged:
+ @*
+ @example
+ cd $@{top_builddir@}/agen5
+ DEBUG_ENABLED=true bash bootstrap.dir expr.ini
+ make CFLAGS='-g -DDEBUG_ENABLED=1'
+ @end example
+
+ Be aware that you cannot rebuild this source in this way without first
+ having installed the @code{autogen} executable in your search path.
+ Because of this, "expr.ini" is in the distributed source list, and
+ not in the dependencies.
+ @end table
+ _EOF_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = trace-out;
+ arg-type = string;
+ arg-name = file;
+ descrip = "tracing output file or filter";
+#ifndef XML2AG
+
+ doc = <<- _EOF_
+ The output specified may be a file name, a file that is appended to,
+ or, if the option argument begins with the @code{pipe} operator
+ (@code{|}), a command that will receive the tracing output as standard
+ in. For example, @code{--traceout='| less'} will run the trace output
+ through the @code{less} program. Appending to a file is specified by
+ preceeding the file name with two greater-than characters (@code{>>}).
+ _EOF_;
+
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+/*
+ * These are for debugging AutoGen itself:
+ */
+flag = {
+ name = show-defs;
+ descrip = "Show the definition tree";
+#ifndef XML2AG
+ no-preset;
+ ifdef = DEBUG_ENABLED;
+ omitted-usage;
+
+ doc = <<- _EOF_
+ This will print out the complete definition tree before processing
+ the template.
+ _EOF_ ;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = used-defines;
+ descrip = "Show the definitions used";
+#ifndef XML2AG
+ no-preset;
+ doc = <<- _EOF_
+ This will print out the names of definition values searched for
+ during the processing of the template, whether actually found or
+ not. There may be other referenced definitions in a template in
+ portions of the template not evaluated. Some of the names listed
+ may be computed names and others AutoGen macro arguments. This is
+ not a means for producing a definitive, all-encompassing list of all
+ and only the values used from a definition file. This is intended
+ as an aid to template documentation only.
+ _EOF_ ;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = core;
+ value = C;
+ descrip = 'Leave a core dump on a failure exit';
+ ifdef = HAVE_SYS_RESOURCE_H;
+
+ doc = <<- _EOF_
+ Many systems default to a zero sized core limit. If the system
+ has the sys/resource.h header and if this option is supplied,
+ then in the failure exit path, autogen will attempt to set the
+ soft core limit to whatever the hard core limit is. If that
+ does not work, then an administrator must raise the hard core
+ size limit.
+ _EOF_;
+};
+
+#ifdef DAEMON_ENABLED
+flag = {
+ name = daemon;
+ value = d;
+ arg-type = string;
+ arg-name = connect-type;
+ descrip = "TCP port or pipe file name";
+ woops = (error "Daemon-ization is not ready for prime time");
+#ifndef XML2AG
+ ifdef = DAEMON_ENABLED;
+ no-preset;
+ flag-code = ' SET_OPT_DEFINITIONS("-");';
+ doc = <<- _EODoc_
+ If you wish to be able to run AutoGen as a daemon process, specify
+ this option. The resulting daemon will be able to receive requests on
+ either a socket or over a named pipe. The syntax of the connect-type
+ argument determines the kind of connection:
+
+ @table @samp
+ @item is a number
+ The address family will default to AF_INET and the number must
+ represent the port number AutoGen will listen on.
+
+ @item contains a colon
+ The string up to the colon will be looked up. If it matches an
+ address family, the string after the colon is presumed to be an
+ address of the appropriate kind. "unix:" addresses should be a
+ path name with existing directory names, but a non-existent base
+ file name. "inet:" and "inet6" should be followed by a port number.
+
+ @item some other string
+ The string represents a named pipe. The name of the bi-directional
+ pipe that any number of processes can open and communicate over. If
+ your system does @strong{not} support such things, then the input pipe
+ will be suffixed with "-in" and the output pipe suffixed with "-out"
+ and only one client may connect at a time.
+ @end table
+ _EODoc_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+#endif // DAEMON_ENABLED
+
+#ifndef XML2AG
+flag = {
+ name = processing;
+ documentation = <<- _EODoc_
+ They specify which outputs and parts of outputs to produce.
+ _EODoc_;
+
+ descrip = <<- _EODoc_
+ These options can be used to control what gets processed
+ in the definitions files and template files
+ _EODoc_;
+};
+#endif
+
+flag = {
+ name = skip-suffix;
+ value = s;
+ arg-type = string;
+ arg-name = suffix;
+ descrip = "Omit the file with this suffix";
+ max = NOLIMIT;
+ flags-cant = select-suffix;
+ stack-arg;
+
+#ifndef XML2AG
+ no-preset;
+ doc = <<- _EOF_
+ Occasionally, it may not be desirable to produce all of the output
+ files specified in the template. (For example, only the @file{.h}
+ header file, but not the @file{.c} program text.) To do this
+ specify @code{--skip-suffix=c} on the command line.
+ _EOF_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = select-suffix;
+ value = o;
+ arg-type = string;
+ arg-name = suffix;
+ descrip = "specify this output suffix";
+ max = NOLIMIT;
+
+#ifndef XML2AG
+ no-preset;
+ doc = <<- _EOF_
+ If you wish to override the suffix specifications in the template,
+ you can use one or more copies of this option. See the suffix
+ specification in the @ref{pseudo macro} section of the info doc.
+ _EOF_;
+ flag-code = <<- _EOCode_
+ char const * arg = pOptDesc->optArg.argString;
+ if ((arg != NULL) && (*arg != NUL))
+ (void)do_suffix(arg, NULL, -1);
+ _EOCode_;
+#else
+ doc = "Pass-through AutoGen argument";
+ stack-arg;
+#endif
+};
+
+flag = {
+ name = define;
+ value = D;
+ arg-type = string;
+ arg-name = value;
+ max = NOLIMIT;
+ descrip = "name to add to definition list";
+ stack-arg;
+#ifndef XML2AG
+ settable;
+ doc = <<- _EODoc_
+ The AutoGen define names are used for the following purposes:
+
+ @enumerate
+ @item
+ Sections of the AutoGen definitions may be enabled or disabled
+ by using C-style #ifdef and #ifndef directives.
+ @item
+ When defining a value for a name, you may specify the index
+ for a particular value. That index may be a literal value,
+ a define option or a value #define-d in the definitions themselves.
+ @item
+ The name of a file may be prefixed with @code{$NAME/}.
+ The @code{$NAME} part of the name string will be replaced with
+ the define-d value for @code{NAME}.
+ @item
+ When AutoGen is finished loading the definitions, the defined values
+ are exported to the environment with, @code{putenv(3)}.
+ These values can then be used in shell scripts with @code{$@{NAME@}}
+ references and in templates with @code{(getenv "NAME")}.
+ @item
+ While processing a template, you may specify an index to retrieve
+ a specific value. That index may also be a define-d value.
+ @end enumerate
+
+ It is entirely equivalent to place this name in the exported environment.
+ Internally, that is what AutoGen actually does with this option.
+ _EODoc_;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+flag = {
+ name = undefine;
+ value = U;
+ arg-type = string;
+ arg-name = name-pat;
+ max = NOLIMIT;
+ descrip = "definition list removal pattern";
+ unstack-arg = define;
+#ifndef XML2AG
+ no-preset;
+ settable;
+ doc = <<- _EOF_
+ Similar to 'C', AutoGen uses @code{#ifdef/#ifndef} preprocessing
+ directives. This option will cause the matching names to be
+ removed from the list of defined values.
+ _EOF_ ;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+#ifndef XML2AG
+flag = {
+ name = dep-track;
+ documentation;
+ descrip = 'This option is used to automate dependency tracking';
+};
+#endif
+
+flag = {
+ name = make-dep;
+ value = M;
+ arg-type = string;
+ arg-name = type;
+ arg-optional;
+ max = NOLIMIT;
+ descrip = "emit make dependency file";
+
+#ifndef XML2AG
+ call-proc = config_dep;
+ settable;
+ no-preset;
+ doc = <<- _EOF_
+
+ This option behaves fairly closely to the way the @code{-M} series of
+ options work with the gcc compiler, except that instead of just
+ emitting the predecessor dependencies, this also emits the successor
+ dependencies (output target files). By default, the output dependency
+ information will be placed in @code{<base-name>.d}, but may also be
+ specified with @code{-MF<file>}. The time stamp on this file will be
+ manipulated so that it will be one second older than the oldest
+ primary output file.
+
+ The target in this dependency file will normally be the dependency
+ file name, but may also be overridden with @code{-MT<targ-name>}.
+ AutoGen will not alter the contents of that file, but it may create
+ it and it will adjust the modification time to match the start time.
+
+ @strong{NB:} these second letters are part of the option argument, so
+ @code{-MF <file>} must have the space character quoted or omitted, and
+ @code{-M "F <file>"} is acceptable because the @code{F} is part of the
+ option argument.
+
+ @code{-M} may be followed by any of the letters M, F, P, T, Q, D, or G.
+ However, only F, Q, T and P are meaningful. All but F have somewhat
+ different meanings. @code{-MT<name>} is interpreted as meaning
+ @code{<name>} is a sentinel file that will depend on all inputs
+ (templates and definition files) and all the output files will depend
+ on this sentinel file. It is suitable for use as a real make target.
+ Q is treated identically to T, except dollar characters ('$') are
+ doubled. P causes a special clean (clobber) phoney rule to be inserted
+ into the make file fragment. An empty rule is always created for
+ building the list of targets.
+
+ This is the recommended usage:
+ @example
+ -MFwhatever-you-like.dep -MTyour-sentinel-file -MP
+ @end example
+ and then in your @code{Makefile}, make the @file{autogen} rule:
+ @example
+ -include whatever-you-like.dep
+ clean_targets += clean-your-sentinel-file
+
+ your-sentinel-file:
+ autogen -MT$@@ -MF$*.d .....
+
+ local-clean :
+ rm -f $(clean_targets)
+ @end example
+
+ The modification time on the dependency file is adjusted to be one
+ second before the earliest time stamp of any other output file.
+ Consequently, it is suitable for use as the sentinel file testifying
+ to the fact the program was successfully run. (@code{-include} is
+ the GNU make way of specifying "include it if it exists". Your make
+ must support that feature or your bootstrap process must create the
+ file.)
+
+ All of this may also be specified using the @code{DEPENDENCIES_OUTPUT}
+ or @code{AUTOGEN_MAKE_DEP} environment variables. If defined,
+ dependency information will be output. If defined with white space
+ free text that is something other than @code{true}, @code{false},
+ @code{yes}, @code{no}, @code{0} or @code{1}, then the string is taken
+ to be an output file name. If it contains a string of white space
+ characters, the first token is as above and the second token is taken
+ to be the target (sentinel) file as @code{-MT} in the paragraphs
+ above. @code{DEPENDENCIES_OUTPUT} will be ignored if there are
+ multiple sequences of white space characters or if its contents are,
+ specifically, @code{false}, @code{no} or @code{0}.
+ _EOF_ ;
+#else
+ doc = "Pass-through AutoGen argument";
+#endif
+};
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Program Documentation
+ */
+#ifndef XML2AG
+option-doc-format = texi;
+
+explain =
+"AutoGen creates text files from templates using external definitions.";
+
+detail = <<- _EndOfDetail_
+ AutoGen is a tool designed for generating program files that contain
+ repetitive text with varied substitutions.
+ _EndOfDetail_;
+
+detail = <<- _EndOfDetail_
+ The definitions (@code{<def-file>}) can be specified with the
+ @code{definitions} option or as the command argument, but not both.
+ Omitting it or specifying @code{-} will result in reading definitions
+ from standard input.
+
+ The output file names are based on the template, but generally use the
+ base name of the definition file. If standard in is read for the
+ definitions, then @code{stdin} will be used for that base name. The
+ suffixes to the base name are gotten from the template. However, the
+ template file may specify the entire output file name. The generated
+ files are always created in the current directory. If you need to
+ place output in an alternate directory, @code{cd} to that directory and
+ use the @code{--templ_dirs} option to search the original directory.
+
+ @code{loop-limit} is used in debugging to stop runaway expansions.
+ _EndOfDetail_;
+
+doc-section = {
+ ds-type = DESCRIPTION;
+ ds-format = texi;
+ ds-text = <<- _END_MAN_DESCRIP
+ @code{AutoGen} is designed for generating program files that contain
+ repetitive text with varied substitutions. The goal is to simplify the
+ maintenance of programs that contain large amounts of repetitious text.
+ This is especially valuable if there are several blocks of such text
+ that must be kept synchronized.
+
+ One common example is the problem of maintaining the code required for
+ processing program options. Processing options requires a minimum of
+ four different constructs be kept in proper order in different places
+ in your program. You need at least: The flag character in the flag
+ string, code to process the flag when it is encountered, a global
+ state variable or two, and a line in the usage text.
+ You will need more things besides this if you choose to implement
+ long option names, configuration file processing, environment variables
+ and so on.
+
+ All of this can be done mechanically; with the proper templates
+ and this program.
+ _END_MAN_DESCRIP;
+};
+
+doc-section = {
+ ds-type = EXAMPLES;
+ ds-format = texi;
+ ds-text = <<- _EndOfMan_
+ Here is how the man page is produced:
+ @example
+ autogen -Tagman-cmd.tpl -MFman-dep -MTstamp-man opts.def
+ @end example
+
+ This command produced this man page from the AutoGen option definition
+ file. It overrides the template specified in @file{opts.def} (normally
+ @file{options.tpl}) and uses @file{agman-cmd.tpl}. It also sets the
+ make file dependency output to @file{man-dep} and the sentinel file
+ (time stamp file) to @file{man-stamp}. The base of the file name is
+ derived from the defined @code{prog-name}.
+
+ The texi invocation document is produced via:
+ @example
+ autogen -Tagtexi-cmd.tpl -MFtexi-dep -MTtexi-stamp opts.def
+ @end example
+ _EndOfMan_;
+};
+#endif /* XML2AG */
+
+/* end of opts.def */
diff --git a/agen5/opts.h b/agen5/opts.h
new file mode 100644
index 0000000..7365da1
--- /dev/null
+++ b/agen5/opts.h
@@ -0,0 +1,305 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (opts.h)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:31 AM by AutoGen 5.16.2pre7
+ * From the definitions opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 36:5:11 templates.
+ *
+ * AutoOpts is a copyrighted work. This header file is not encumbered
+ * by AutoOpts licensing, but is provided under the licensing terms chosen
+ * by the autogen author or copyright holder. AutoOpts is
+ * licensed under the terms of the LGPL. The redistributable library
+ * (``libopts'') is licensed under the terms of either the LGPL or, at the
+ * users discretion, the BSD license. See the AutoOpts and/or libopts sources
+ * for details.
+ *
+ * The autogen program is copyrighted and licensed
+ * under the following terms:
+ *
+ * Copyright (C) 1992-2012 Bruce Korb, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the
+ * GNU General Public License, version 3 or later
+ * <http://gnu.org/licenses/gpl.html>
+ *
+ * autogen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * autogen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/*
+ * This file contains the programmatic interface to the Automated
+ * Options generated for the autogen program.
+ * These macros are documented in the AutoGen info file in the
+ * "AutoOpts" chapter. Please refer to that doc for usage help.
+ */
+#ifndef AUTOOPTS_OPTS_H_GUARD
+#define AUTOOPTS_OPTS_H_GUARD 1
+#include "config.h"
+#include <autoopts/options.h>
+#include <stdarg.h>
+
+/*
+ * Ensure that the library used for compiling this generated header is at
+ * least as new as the version current when the header template was released
+ * (not counting patch version increments). Also ensure that the oldest
+ * tolerable version is at least as old as what was current when the header
+ * template was released.
+ */
+#define AO_TEMPLATE_VERSION 147461
+#if (AO_TEMPLATE_VERSION < OPTIONS_MINIMUM_VERSION) \
+ || (AO_TEMPLATE_VERSION > OPTIONS_STRUCT_VERSION)
+# error option template version mismatches autoopts/options.h header
+ Choke Me.
+#endif
+
+/*
+ * Enumeration of each option:
+ */
+typedef enum {
+ INDEX_OPT_TEMPL_DIRS = 1,
+ INDEX_OPT_OVERRIDE_TPL = 2,
+ INDEX_OPT_LIB_TEMPLATE = 3,
+ INDEX_OPT_DEFINITIONS = 4,
+ INDEX_OPT_LOAD_SCHEME = 5,
+ INDEX_OPT_LOAD_FUNCTIONS = 6,
+ INDEX_OPT_SHELL = 7,
+ INDEX_OPT_NO_FMEMOPEN = 8,
+ INDEX_OPT_EQUATE = 9,
+ INDEX_OPT_BASE_NAME = 11,
+ INDEX_OPT_SOURCE_TIME = 12,
+ INDEX_OPT_WRITABLE = 13,
+ INDEX_OPT_LOOP_LIMIT = 15,
+ INDEX_OPT_TIMEOUT = 16,
+ INDEX_OPT_TRACE = 17,
+ INDEX_OPT_TRACE_OUT = 18,
+ INDEX_OPT_SHOW_DEFS = 19,
+ INDEX_OPT_USED_DEFINES = 20,
+ INDEX_OPT_CORE = 21,
+ INDEX_OPT_SKIP_SUFFIX = 23,
+ INDEX_OPT_SELECT_SUFFIX = 24,
+ INDEX_OPT_DEFINE = 25,
+ INDEX_OPT_UNDEFINE = 26,
+ INDEX_OPT_MAKE_DEP = 28,
+ INDEX_OPT_RESET_OPTION = 29,
+ INDEX_OPT_VERSION = 30,
+ INDEX_OPT_HELP = 31,
+ INDEX_OPT_MORE_HELP = 32,
+ INDEX_OPT_USAGE = 33,
+ INDEX_OPT_SAVE_OPTS = 34,
+ INDEX_OPT_LOAD_OPTS = 35
+} teOptIndex;
+
+#define OPTION_CT 36
+#define AUTOGEN_VERSION "5.16.2"
+#define AUTOGEN_FULL_VERSION "autogen (GNU AutoGen) 5.16.2"
+
+/*
+ * Interface defines for all options. Replace "n" with the UPPER_CASED
+ * option name (as in the teOptIndex enumeration above).
+ * e.g. HAVE_OPT(INPUT_SELECT)
+ */
+#define DESC(n) (autogenOptions.pOptDesc[INDEX_OPT_## n])
+#define HAVE_OPT(n) (! UNUSED_OPT(& DESC(n)))
+#define OPT_ARG(n) (DESC(n).optArg.argString)
+#define STATE_OPT(n) (DESC(n).fOptState & OPTST_SET_MASK)
+#define COUNT_OPT(n) (DESC(n).optOccCt)
+#define ISSEL_OPT(n) (SELECTED_OPT(&DESC(n)))
+#define ISUNUSED_OPT(n) (UNUSED_OPT(& DESC(n)))
+#define ENABLED_OPT(n) (! DISABLED_OPT(& DESC(n)))
+#define STACKCT_OPT(n) (((tArgList*)(DESC(n).optCookie))->useCt)
+#define STACKLST_OPT(n) (((tArgList*)(DESC(n).optCookie))->apzArgs)
+#define CLEAR_OPT(n) STMTS( \
+ DESC(n).fOptState &= OPTST_PERSISTENT_MASK; \
+ if ((DESC(n).fOptState & OPTST_INITENABLED) == 0) \
+ DESC(n).fOptState |= OPTST_DISABLED; \
+ DESC(n).optCookie = NULL )
+
+/* * * * * *
+ *
+ * Enumeration of autogen exit codes
+ */
+typedef enum {
+ AUTOGEN_EXIT_SUCCESS = 0,
+ AUTOGEN_EXIT_OPTION_ERROR = 1,
+ AUTOGEN_EXIT_BAD_TEMPLATE = 2,
+ AUTOGEN_EXIT_BAD_DEFINITIONS = 3,
+ AUTOGEN_EXIT_LOAD_ERROR = 4,
+ AUTOGEN_EXIT_SIGNAL = 5,
+ AUTOGEN_EXIT_NO_CONFIG_INPUT = 66,
+ AUTOGEN_EXIT_LIBOPTS_FAILURE = 70
+} autogen_exit_code_t;
+/* * * * * *
+ *
+ * Interface defines for specific options.
+ */
+#define VALUE_OPT_TEMPL_DIRS 'L'
+
+#define SET_OPT_TEMPL_DIRS(a) STMTS( \
+ DESC(TEMPL_DIRS).optActualIndex = 1; \
+ DESC(TEMPL_DIRS).optActualValue = VALUE_OPT_TEMPL_DIRS; \
+ DESC(TEMPL_DIRS).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(TEMPL_DIRS).fOptState |= OPTST_SET; \
+ DESC(TEMPL_DIRS).optArg.argString = (a); \
+ (*(DESC(TEMPL_DIRS).pOptProc))(&autogenOptions, \
+ autogenOptions.pOptDesc + 1); )
+#define VALUE_OPT_OVERRIDE_TPL 'T'
+#define VALUE_OPT_LIB_TEMPLATE 'l'
+#define VALUE_OPT_DEFINITIONS 4
+
+#define SET_OPT_DEFINITIONS(a) STMTS( \
+ DESC(DEFINITIONS).optActualIndex = 4; \
+ DESC(DEFINITIONS).optActualValue = VALUE_OPT_DEFINITIONS; \
+ DESC(DEFINITIONS).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(DEFINITIONS).fOptState |= OPTST_SET; \
+ DESC(DEFINITIONS).optArg.argString = (a) )
+#define DISABLE_OPT_DEFINITIONS STMTS( \
+ DESC(DEFINITIONS).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(DEFINITIONS).fOptState |= OPTST_SET | OPTST_DISABLED; \
+ DESC(DEFINITIONS).optArg.argString = NULL )
+#define VALUE_OPT_LOAD_SCHEME 'S'
+
+#define SET_OPT_LOAD_SCHEME(a) STMTS( \
+ DESC(LOAD_SCHEME).optActualIndex = 5; \
+ DESC(LOAD_SCHEME).optActualValue = VALUE_OPT_LOAD_SCHEME; \
+ DESC(LOAD_SCHEME).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(LOAD_SCHEME).fOptState |= OPTST_SET; \
+ DESC(LOAD_SCHEME).optArg.argString = (a); \
+ (*(DESC(LOAD_SCHEME).pOptProc))(&autogenOptions, \
+ autogenOptions.pOptDesc + 5); )
+#define VALUE_OPT_LOAD_FUNCTIONS 'F'
+#define VALUE_OPT_SHELL 7
+#define VALUE_OPT_NO_FMEMOPEN 'm'
+#define VALUE_OPT_EQUATE 9
+#define VALUE_OPT_BASE_NAME 'b'
+#define VALUE_OPT_SOURCE_TIME 12
+#define VALUE_OPT_WRITABLE 13
+
+#define SET_OPT_WRITABLE STMTS( \
+ DESC(WRITABLE).optActualIndex = 13; \
+ DESC(WRITABLE).optActualValue = VALUE_OPT_WRITABLE; \
+ DESC(WRITABLE).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(WRITABLE).fOptState |= OPTST_SET )
+#define DISABLE_OPT_WRITABLE STMTS( \
+ DESC(WRITABLE).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(WRITABLE).fOptState |= OPTST_SET | OPTST_DISABLED; \
+ DESC(WRITABLE).optArg.argString = NULL )
+#define VALUE_OPT_LOOP_LIMIT 15
+
+#define OPT_VALUE_LOOP_LIMIT (DESC(LOOP_LIMIT).optArg.argInt)
+#define VALUE_OPT_TIMEOUT 't'
+#ifdef SHELL_ENABLED
+#define OPT_VALUE_TIMEOUT (DESC(TIMEOUT).optArg.argInt)
+#endif /* SHELL_ENABLED */
+#define VALUE_OPT_TRACE 17
+
+typedef enum {
+ TRACE_NOTHING, TRACE_DEBUG_MESSAGE, TRACE_SERVER_SHELL,
+ TRACE_TEMPLATES, TRACE_BLOCK_MACROS, TRACE_EXPRESSIONS,
+ TRACE_EVERYTHING
+} te_Trace;
+#define OPT_TRACE_VAL2STR(_v) optionKeywordName(&DESC(TRACE), (_v))
+#define OPT_VALUE_TRACE (DESC(TRACE).optArg.argEnum)
+#define VALUE_OPT_TRACE_OUT 18
+#define VALUE_OPT_SHOW_DEFS 19
+#define VALUE_OPT_USED_DEFINES 20
+#define VALUE_OPT_CORE 'C'
+#define VALUE_OPT_SKIP_SUFFIX 's'
+#define VALUE_OPT_SELECT_SUFFIX 'o'
+#define VALUE_OPT_DEFINE 'D'
+
+#define SET_OPT_DEFINE(a) STMTS( \
+ DESC(DEFINE).optActualIndex = 25; \
+ DESC(DEFINE).optActualValue = VALUE_OPT_DEFINE; \
+ DESC(DEFINE).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(DEFINE).fOptState |= OPTST_SET; \
+ DESC(DEFINE).optArg.argString = (a); \
+ (*(DESC(DEFINE).pOptProc))(&autogenOptions, \
+ autogenOptions.pOptDesc + 25); )
+#define VALUE_OPT_UNDEFINE 'U'
+
+#define SET_OPT_UNDEFINE(a) STMTS( \
+ DESC(DEFINE).optActualIndex = 26; \
+ DESC(DEFINE).optActualValue = VALUE_OPT_UNDEFINE; \
+ DESC(DEFINE).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(DEFINE).fOptState |= OPTST_SET | OPTST_EQUIVALENCE; \
+ DESC(DEFINE).optArg.argString = (a); \
+ (*(DESC(UNDEFINE).pOptProc))(&autogenOptions, \
+ autogenOptions.pOptDesc + INDEX_OPT_DEFINE); )
+#define VALUE_OPT_MAKE_DEP 'M'
+
+#define SET_OPT_MAKE_DEP(a) STMTS( \
+ DESC(MAKE_DEP).optActualIndex = 28; \
+ DESC(MAKE_DEP).optActualValue = VALUE_OPT_MAKE_DEP; \
+ DESC(MAKE_DEP).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(MAKE_DEP).fOptState |= OPTST_SET; \
+ DESC(MAKE_DEP).optArg.argString = (a); \
+ (*(DESC(MAKE_DEP).pOptProc))(&autogenOptions, \
+ autogenOptions.pOptDesc + 28); )
+#define VALUE_OPT_HELP '?'
+#define VALUE_OPT_MORE_HELP '!'
+#define VALUE_OPT_RESET_OPTION 'R'
+#define VALUE_OPT_VERSION 'v'
+#define VALUE_OPT_USAGE 'u'
+#define VALUE_OPT_SAVE_OPTS '>'
+#define VALUE_OPT_LOAD_OPTS '<'
+#define SET_OPT_SAVE_OPTS(a) STMTS( \
+ DESC(SAVE_OPTS).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(SAVE_OPTS).fOptState |= OPTST_SET; \
+ DESC(SAVE_OPTS).optArg.argString = (char const*)(a) )
+/*
+ * Interface defines not associated with particular options
+ */
+#define ERRSKIP_OPTERR STMTS(autogenOptions.fOptSet &= ~OPTPROC_ERRSTOP)
+#define ERRSTOP_OPTERR STMTS(autogenOptions.fOptSet |= OPTPROC_ERRSTOP)
+#define RESTART_OPT(n) STMTS( \
+ autogenOptions.curOptIdx = (n); \
+ autogenOptions.pzCurOpt = NULL)
+#define START_OPT RESTART_OPT(1)
+#define USAGE(c) (*autogenOptions.pUsageProc)(&autogenOptions, c)
+/* extracted from opthead.tlib near line 484 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * global exported definitions
+ */
+extern char const * tpl_fname;
+extern bool trace_is_to_pipe;
+
+extern void vusage_message(char const * fmt, va_list ap);
+extern void usage_message(char const * fmt, ...);
+
+
+/* * * * * *
+ *
+ * Declare the autogen option descriptor.
+ */
+extern tOptions autogenOptions;
+# define OPT_NO_XLAT_CFG_NAMES
+# define OPT_NO_XLAT_OPT_NAMES
+
+# define OPT_XLAT_CFG_NAMES
+# define OPT_XLAT_OPT_NAMES
+
+# ifndef _
+# define _(_s) _s
+# endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* AUTOOPTS_OPTS_H_GUARD */
+/* opts.h ends here */
diff --git a/agen5/proto.h b/agen5/proto.h
new file mode 100644
index 0000000..2423ec3
--- /dev/null
+++ b/agen5/proto.h
@@ -0,0 +1,300 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * Prototypes for agen5
+ * Generated Sat Aug 11 09:41:33 PDT 2012
+ */
+#ifndef AGEN5_PROTO_H_GUARD
+#define AGEN5_PROTO_H_GUARD 1
+
+#ifndef LOCAL
+# define LOCAL extern
+# define REDEF_LOCAL 1
+#else
+# undef REDEF_LOCAL
+#endif
+/*
+ * Extracted from agCgi.c
+ */
+LOCAL void
+loadCgi(void);
+
+/*
+ * Extracted from agDep.c
+ */
+LOCAL void
+add_source_file(char const * pz);
+
+LOCAL void
+rm_source_file(char const * pz);
+
+LOCAL void
+add_target_file(char const * pz);
+
+LOCAL void
+rm_target_file(char const * pz);
+
+LOCAL void
+start_dep_file(void);
+
+LOCAL void
+wrap_up_depends(void);
+
+/*
+ * Extracted from agInit.c
+ */
+LOCAL void
+initialize(int arg_ct, char** arg_vec);
+
+LOCAL void
+config_dep(tOptions * opts, tOptDesc * od);
+
+/*
+ * Extracted from agShell.c
+ */
+LOCAL void
+close_server_shell(void);
+
+LOCAL char *
+shell_cmd(char const * cmd);
+
+/*
+ * Extracted from agUtils.c
+ */
+LOCAL char *
+aprf(char const * pzFmt, ...);
+
+LOCAL void
+open_trace_file(char ** av, tOptDesc * odsc);
+
+LOCAL void
+check_make_dep_env(void);
+
+LOCAL void
+process_ag_opts(int arg_ct, char ** arg_vec);
+
+LOCAL char const *
+get_define_str(char const * de_name, bool check_env);
+
+LOCAL char *
+span_quote(char * in_q);
+
+LOCAL char const *
+skip_scheme(char const * scan, char const * end);
+
+LOCAL int
+count_nl(char const * pz);
+
+LOCAL char const *
+skip_expr(char const * pzSrc, size_t len);
+
+/*
+ * Extracted from autogen.c
+ */
+LOCAL void
+ag_abend_at(char const * pzMsg
+#ifdef DEBUG_ENABLED
+ , char const * pzFile, int line
+#endif
+ );
+
+LOCAL void *
+ao_malloc (size_t sz);
+
+LOCAL void *
+ao_realloc (void *p, size_t sz);
+
+LOCAL char *
+ao_strdup (char const * str);
+
+/*
+ * Extracted from defDirect.c
+ */
+LOCAL char*
+processDirective(char* pzScan);
+
+/*
+ * Extracted from defFind.c
+ */
+LOCAL int
+canonical_name(char * pzD, char const * pzS, int srcLen);
+
+LOCAL def_ent_t *
+find_def_ent(char * name, bool * indexed);
+
+LOCAL void
+print_used_defines(void);
+
+LOCAL def_ent_t **
+find_def_ent_list(char * name);
+
+/*
+ * Extracted from defLex.c
+ */
+LOCAL te_dp_event
+yylex(void);
+
+LOCAL void
+yyerror(char* s);
+
+/*
+ * Extracted from defLoad.c
+ */
+LOCAL def_ent_t *
+new_def_ent(void);
+
+LOCAL void
+print_ent(def_ent_t * pDef);
+
+LOCAL def_ent_t *
+number_and_insert_ent(char * name, char const * idx_str);
+
+LOCAL void
+read_defs(void);
+
+LOCAL void
+unload_defs(void);
+
+/*
+ * Extracted from expGuile.c
+ */
+LOCAL teGuileType
+ag_scm_type_e(SCM typ);
+
+LOCAL SCM
+ag_scm_c_eval_string_from_file_line(
+ char const * pzExpr, char const * pzFile, int line);
+
+/*
+ * Extracted from expOutput.c
+ */
+LOCAL void
+make_readonly(void);
+
+LOCAL void
+open_output_file(char const * fname, size_t nmsz, char const * mode, int flags);
+
+/*
+ * Extracted from expPrint.c
+ */
+LOCAL SCM
+run_printf(char const * pzFmt, int len, SCM alist);
+
+/*
+ * Extracted from expString.c
+ */
+LOCAL void
+do_multi_subs(char ** ppzStr, ssize_t * pStrLen, SCM match, SCM repl);
+
+/*
+ * Extracted from funcDef.c
+ */
+LOCAL void
+parse_mac_args(templ_t * pT, macro_t * mac);
+
+/*
+ * Extracted from funcEval.c
+ */
+LOCAL char const *
+scm2display(SCM s);
+
+LOCAL char const *
+eval_mac_expr(bool * allocated);
+
+LOCAL SCM
+eval(char const * expr);
+
+/*
+ * Extracted from funcFor.c
+ */
+LOCAL for_state_t *
+new_for_context(void);
+
+LOCAL void
+free_for_context(bool everything);
+
+/*
+ * Extracted from functions.c
+ */
+LOCAL loop_jmp_type_t
+call_gen_block(jmp_buf jbuf, templ_t * tpl, macro_t * mac, macro_t * end_mac);
+
+LOCAL void
+gen_new_block(templ_t * tpl);
+
+LOCAL macro_t *
+mLoad_Leave(templ_t * tpl, macro_t * mac, char const ** p_scan);
+
+/*
+ * Extracted from loadPseudo.c
+ */
+LOCAL char const *
+do_suffix(char const * const pzData, char const * pzFileName, int lineNo);
+
+LOCAL char const *
+loadPseudoMacro(char const * pzData, char const * pzFileName);
+
+/*
+ * Extracted from scmStrings.c
+ */
+LOCAL void
+ag_scribble_init(void);
+
+LOCAL void
+ag_scribble_deinit(void);
+
+LOCAL void
+ag_scribble_free(void);
+
+LOCAL char *
+ag_scribble(ssize_t size);
+
+LOCAL char *
+ag_scm2zchars(SCM s, const char * type);
+
+/*
+ * Extracted from tpLoad.c
+ */
+LOCAL templ_t *
+find_tpl(char const * pzTemplName);
+
+LOCAL tSuccess
+find_file(char const * in_name,
+ char * res_name,
+ char const * const * sfx_list,
+ char const * referring_tpl);
+
+LOCAL templ_t *
+tpl_load(char const * fname, char const * referrer);
+
+LOCAL void
+tpl_unload(templ_t * tpl);
+
+LOCAL void
+cleanup(templ_t * tpl);
+
+/*
+ * Extracted from tpParse.c
+ */
+LOCAL macro_t *
+parse_tpl(macro_t * mac, char const ** p_scan);
+
+/*
+ * Extracted from tpProcess.c
+ */
+LOCAL void
+gen_block(templ_t * tpl, macro_t * mac, macro_t * emac);
+
+LOCAL out_spec_t *
+next_out_spec(out_spec_t * os);
+
+LOCAL void
+process_tpl(templ_t * tpl);
+
+LOCAL void
+out_close(bool purge);
+
+#ifdef REDEF_LOCAL
+# undef LOCAL
+# define LOCAL
+#endif
+#endif /* AGEN5_PROTO_H_GUARD */
diff --git a/agen5/pseudo-fsm.h b/agen5/pseudo-fsm.h
new file mode 100644
index 0000000..338022f
--- /dev/null
+++ b/agen5/pseudo-fsm.h
@@ -0,0 +1,227 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (pseudo-fsm.h)
+ *
+ * It has been AutoGen-ed August 11, 2012 at 09:41:36 AM by AutoGen 5.16.2pre7
+ * From the definitions pseudo.def
+ * and the template file fsm
+ *
+ * Automated Finite State Machine
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name ``Bruce Korb'' nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * AutoFSM IS PROVIDED BY Bruce Korb ``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 Bruce Korb OR ANY OTHER CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * This file enumerates the states and transition events for a FSM.
+ *
+ * te_pm_state
+ * The available states. FSS_INIT is always defined to be zero
+ * and FSS_INVALID and FSS_DONE are always made the last entries.
+ *
+ * te_pm_event
+ * The transition events. These enumerate the event values used
+ * to select the next state from the current state.
+ * PM_EV_INVALID is always defined at the end.
+ */
+#ifndef AUTOFSM_PSEUDO_FSM_H_GUARD
+#define AUTOFSM_PSEUDO_FSM_H_GUARD 1
+
+/*
+ * Finite State machine States
+ *
+ * Count of non-terminal states. The generated states INVALID and DONE
+ * are terminal, but INIT is not :-).
+ */
+#define PM_STATE_CT 5
+typedef enum {
+ PM_ST_INIT, PM_ST_ST_MARK, PM_ST_AGEN, PM_ST_TEMPL,
+ PM_ST_END_MARK, PM_ST_INVALID, PM_ST_DONE
+} te_pm_state;
+
+/*
+ * Finite State machine transition Events.
+ *
+ * Count of the valid transition events
+ */
+#define PM_EVENT_CT 7
+typedef enum {
+ PM_EV_ED_MODE, PM_EV_MARKER, PM_EV_END_PSEUDO, PM_EV_AUTOGEN,
+ PM_EV_TEMPLATE, PM_EV_SUFFIX, PM_EV_SCHEME, PM_EV_INVALID
+} te_pm_event;
+
+/*
+ * Enumeration of the valid transition types
+ * Some transition types may be common to several transitions.
+ */
+typedef enum {
+ PM_TR_INIT_MARKER,
+ PM_TR_INVALID,
+ PM_TR_NOOP,
+ PM_TR_SKIP_ED_MODE,
+ PM_TR_TEMPL_MARKER,
+ PM_TR_TEMPL_SCHEME,
+ PM_TR_TEMPL_SUFFIX
+} te_pm_trans;
+#define PM_TRANSITION_CT 7
+
+/**
+ * State transition handling map. Map the state enumeration and the event
+ * enumeration to the new state and the transition enumeration code (in that
+ * order). It is indexed by first the current state and then the event code.
+ */
+typedef struct pm_transition t_pm_transition;
+struct pm_transition {
+ te_pm_state next_state;
+ te_pm_trans transition;
+};
+
+#ifndef DEFINE_FSM
+extern const t_pm_transition pm_trans_table[ PM_STATE_CT ][ PM_EVENT_CT ];
+
+extern int
+pm_invalid_transition( te_pm_state st, te_pm_event evt );
+#else
+const t_pm_transition
+pm_trans_table[ PM_STATE_CT ][ PM_EVENT_CT ] = {
+
+ /* STATE 0: PM_ST_INIT */
+ { { PM_ST_INIT, PM_TR_SKIP_ED_MODE }, /* EVT: -*- */
+ { PM_ST_ST_MARK, PM_TR_INIT_MARKER }, /* EVT: MARKER */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: END_PSEUDO */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: AUTOGEN */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: TEMPLATE */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: SUFFIX */
+ { PM_ST_INVALID, PM_TR_INVALID } /* EVT: SCHEME */
+ },
+
+
+ /* STATE 1: PM_ST_ST_MARK */
+ { { PM_ST_ST_MARK, PM_TR_SKIP_ED_MODE }, /* EVT: -*- */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: MARKER */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: END_PSEUDO */
+ { PM_ST_AGEN, PM_TR_NOOP }, /* EVT: AUTOGEN */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: TEMPLATE */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: SUFFIX */
+ { PM_ST_INVALID, PM_TR_INVALID } /* EVT: SCHEME */
+ },
+
+
+ /* STATE 2: PM_ST_AGEN */
+ { { PM_ST_AGEN, PM_TR_SKIP_ED_MODE }, /* EVT: -*- */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: MARKER */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: END_PSEUDO */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: AUTOGEN */
+ { PM_ST_TEMPL, PM_TR_NOOP }, /* EVT: TEMPLATE */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: SUFFIX */
+ { PM_ST_INVALID, PM_TR_INVALID } /* EVT: SCHEME */
+ },
+
+
+ /* STATE 3: PM_ST_TEMPL */
+ { { PM_ST_TEMPL, PM_TR_SKIP_ED_MODE }, /* EVT: -*- */
+ { PM_ST_END_MARK, PM_TR_TEMPL_MARKER }, /* EVT: MARKER */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: END_PSEUDO */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: AUTOGEN */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: TEMPLATE */
+ { PM_ST_TEMPL, PM_TR_TEMPL_SUFFIX }, /* EVT: SUFFIX */
+ { PM_ST_TEMPL, PM_TR_TEMPL_SCHEME } /* EVT: SCHEME */
+ },
+
+
+ /* STATE 4: PM_ST_END_MARK */
+ { { PM_ST_END_MARK, PM_TR_INVALID }, /* EVT: -*- */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: MARKER */
+ { PM_ST_DONE, PM_TR_NOOP }, /* EVT: END_PSEUDO */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: AUTOGEN */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: TEMPLATE */
+ { PM_ST_INVALID, PM_TR_INVALID }, /* EVT: SUFFIX */
+ { PM_ST_INVALID, PM_TR_INVALID } /* EVT: SCHEME */
+ }
+};
+#define PmFsmErr_off 19
+#define PmEvInvalid_off 75
+#define PmStInit_off 83
+
+
+static char const zPmStrings[169] =
+/* 0 */ "** OUT-OF-RANGE **\0"
+/* 19 */ "FSM Error: in state %d (%s), event %d (%s) is invalid\n\0"
+/* 75 */ "invalid\0"
+/* 83 */ "init\0"
+/* 88 */ "st_mark\0"
+/* 96 */ "agen\0"
+/* 101 */ "templ\0"
+/* 107 */ "end_mark\0"
+/* 116 */ "-*-\0"
+/* 120 */ "marker\0"
+/* 127 */ "end_pseudo\0"
+/* 138 */ "autogen\0"
+/* 146 */ "template\0"
+/* 155 */ "suffix\0"
+/* 162 */ "scheme";
+
+static const size_t aszPmStates[5] = {
+ 83, 88, 96, 101, 107 };
+
+static const size_t aszPmEvents[8] = {
+ 116, 120, 127, 138, 146, 155, 162, 75 };
+
+
+#define PM_EVT_NAME(t) ( (((unsigned)(t)) >= 8) \
+ ? zPmStrings : zPmStrings + aszPmEvents[t])
+
+#define PM_STATE_NAME(s) ( (((unsigned)(s)) >= 5) \
+ ? zPmStrings : zPmStrings + aszPmStates[s])
+
+#ifndef EXIT_FAILURE
+# define EXIT_FAILURE 1
+#endif
+
+static int pm_invalid_transition( te_pm_state st, te_pm_event evt );
+
+/* * * * * * * * * THE CODE STARTS HERE * * * * * * * *
+ *
+ * Print out an invalid transition message and return EXIT_FAILURE
+ */
+static int
+pm_invalid_transition( te_pm_state st, te_pm_event evt )
+{
+ /* START == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */
+ char const * fmt = zPmStrings + PmFsmErr_off;
+ fprintf( stderr, fmt, st, PM_STATE_NAME(st), evt, PM_EVT_NAME(evt));
+ /* END == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */
+
+ return EXIT_FAILURE;
+}
+
+#endif /* DEFINE_FSM */
+
+#endif /* AUTOFSM_PSEUDO_FSM_H_GUARD */
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of pseudo-fsm.h */
diff --git a/agen5/pseudo.def b/agen5/pseudo.def
new file mode 100644
index 0000000..8051e64
--- /dev/null
+++ b/agen5/pseudo.def
@@ -0,0 +1,69 @@
+/* -*- Mode: C -*- */
+
+/**
+ * \file pseudo.def
+ *
+ * Time-stamp: "2010-07-03 09:51:09 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This defines the finite state transition table for parsing the
+ * pseudo-macro at the head of every template
+ */
+AutoGen Definitions fsm;
+
+/* "method" is not defined -- produce a header only */
+
+prefix = pm;
+
+event = ed_mode; /* the -*- mode marker */
+event = marker; /* a start or end macro mark */
+event = end_pseudo; /* newline found in end_mark state */
+
+event = "autogen"; /* "autogen" */
+event = template; /* "template" */
+event = suffix; /* a string other than the above */
+event = scheme; /* a scheme expression to process */
+
+state = st_mark; /* start marker processed */
+state = agen; /* "autogen5" processed */
+state = templ; /* "template" processed */
+state = end_mark; /* end marker processed */
+
+ed-mode = '-*-';
+
+/*
+ * No-op transition. Applies to all states and does not change state.
+ */
+transition = { tst = '*'; tev = ed_mode; ttype = skip_ed_mode; };
+/*
+ * Disable the ed_mode transition in the end_mark state tho
+ */
+transition = { tst = end_mark; tev = ed_mode; ttype = invalid; };
+
+/*
+ * Meaningful transitions
+ */
+transition = { tst = init; tev = marker; next = st_mark; };
+transition = { tst = st_mark; tev = "autogen"; next = agen; ttype = noop; };
+transition = { tst = agen; tev = template; next = templ; ttype = noop; };
+transition = { tst = templ; tev = suffix; };
+transition = { tst = templ; tev = scheme; };
+transition = { tst = templ; tev = marker; next = end_mark; };
+transition = { tst = end_mark; tev = end_pseudo; next = done; ttype = noop; };
+
+/* end of agen5/pseudo.def */
diff --git a/agen5/schemedef.scm b/agen5/schemedef.scm
new file mode 100644
index 0000000..f87f04b
--- /dev/null
+++ b/agen5/schemedef.scm
@@ -0,0 +1,388 @@
+
+;;; Time-stamp: "2012-04-14 08:39:41 bkorb"
+;;;
+;;; This file is part of AutoGen.
+;;; AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+;;;
+;;; AutoGen is free software: you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by the
+;;; Free Software Foundation, either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; AutoGen is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+;;; See the GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License along
+;;; with this program. If not, see <http://www.gnu.org/licenses/>.
+;;;
+;;; This module defines all the scheme initialization for AutoGen.
+;;; It gets sucked up into directives.h as a single ANSI-C string.
+;;; Comments, blank lines and leading white space are omitted.
+;;;
+;;; The contents of this file get converted to a C language static string
+;;; and is then fed to the guile library at start up time.
+;;; Blank lines, commends, leading and trailing white space and spaces
+;;; before closing parentheses are all stripped.
+;;;
+(use-modules (ice-9 common-list))
+
+(define identifier?
+ (lambda (x) (or (string? x) (symbol? x))))
+
+(define normalize-identifier
+ (lambda (x)
+ (if (string? x) (string->symbol x) x)))
+
+(define coerce->string
+ (lambda (x)
+ (let ((char->string (lambda (x) (make-string 1 x)))
+ (coercable? (lambda (x)
+ (or (string? x) (boolean? x) (char? x)
+ (symbol? x) (list? x) (number? x)) )) )
+
+ (if (not (coercable? x))
+ (error "Wrong type to coerce->string" x))
+
+ (cond
+ ((string? x) (string-append
+ (char->string #\") x (char->string #\") ))
+
+ ; Probably not what was wanted, but fun
+ ((boolean? x) (if x "#t" "#f"))
+ ((char? x) (char->string x))
+ ((number? x) (number->string x))
+ ((symbol? x) (symbol->string x))
+ ((list? x) (if (every coercable? x)
+ (apply string-append (map coerce->string x)) ))
+) ) ) )
+
+
+;;; alist->autogen-def:
+;;; take a scheme alist of values, and create autogen assignments.
+;;; recursive alists are handled. Using a bare list as a value to be
+;;; assigned is not a terribly good idea, though it should work if it
+;;; doesn't look too much like an alist The returned string doesn't
+;;; contain opening and closing brackets.
+
+(define alist->autogen-def
+ (lambda (lst . recursive)
+ (if (null? recursive) (set! recursive #f)
+ (set! recursive #t))
+ (let ((res (if recursive "{\n" ""))
+ (list-nnul? (lambda (x) (and (list? x) (not (null? x))))))
+ (do ((i lst (cdr i)))
+ ((null? i) (if recursive
+ (string-append res "}")
+ res))
+ (let* ((kvpair (car i))
+ (value (cdr kvpair))
+ (value-is-alist (if (and (list-nnul? value)
+ (list-nnul? (car value))
+ (list-nnul? (caar value))
+ (identifier? (caaar value)))
+ #t #f)))
+ (set! res (string-append res
+ (coerce->string (normalize-identifier (car kvpair)))
+ " = "
+ (if value-is-alist
+ (alist->autogen-def (car value) 1)
+ (coerce->string (cdr kvpair)))
+ ";\n"
+) ) ) ) ) ) )
+
+(define shell-cleanup "")
+(define add-cleanup (lambda (t)
+ (set! shell-cleanup (string-append shell-cleanup "\n" t "\n")) ))
+(define tmp-dir "")
+
+(define header-file "")
+(define header-guard "")
+(define autogen-version "AUTOGEN_VERSION")
+(define c-file-line-fmt "#line %2$d \"%1$s\"\n")
+
+(define-macro (defined-as predicate symbol)
+ `(and (defined? ',symbol) (,predicate ,symbol)))
+
+;;; /*=gfunc html_escape_encode
+;;; *
+;;; * what: encode html special characters
+;;; * general-use:
+;;; *
+;;; * exparg: str , string to make substitutions in
+;;; *
+;;; * doc: This function will replace replace the characters @code{'&'},
+;;; * @code{'<'} and @code{'>'} characters with the HTML/XML
+;;; * escape-encoded strings (@code{"&amp;"}, @code{"&lt;"}, and
+;;; * @code{"&gt;"}, respectively).
+;;; =*/
+;;;
+(define html-escape-encode (lambda (str)
+ (string-substitute str
+ '("&" "<" ">")
+ '("&amp;" "&lt;" "&gt;") ) ))
+
+(define stt-table (make-hash-table 31))
+(define stt-curr stt-table)
+(define stt-idx-tbl stt-table)
+(define stt-idx 0)
+
+;;; /*=gfunc string_table_new
+;;; *
+;;; * what: create a string table
+;;; * general-use:
+;;; *
+;;; * exparg: st-name , the name of the array of characters
+;;; *
+;;; * doc:
+;;; * This function will create an array of characters. The companion
+;;; * functions, (@xref{SCM string-table-add},
+;;; * @xref{SCM string-table-add-ref}, and
+;;; * @pxref{SCM emit-string-table}) will insert text and emit the
+;;; * populated table.
+;;; *
+;;; * With these functions, it should be much easier to construct
+;;; * structures containing string offsets instead of string pointers.
+;;; * That can be very useful when transmitting, storing or sharing data
+;;; * with different address spaces.
+;;; *
+;;; * @noindent
+;;; * Here is a brief example copied from the strtable.test test:
+;;; *
+;;; * @example
+;;; * [+ (string-table-new "scribble")
+;;; * `' (out-push-new) ;; redirect output to temporary
+;;; * `' (define ct 1) +][+
+;;; *
+;;; * FOR str IN that was the week that was +][+
+;;; * `' (set! ct (+ ct 1))
+;;; * +]
+;;; * `' [+ (string-table-add-ref "scribble" (get "str")) +],[+
+;;; * ENDFOR +]
+;;; * [+ (out-suspend "main")
+;;; * `' (emit-string-table "scribble")
+;;; * `' (ag-fprintf 0 "\nchar const *ap[%d] = @{" ct)
+;;; * `' (out-resume "main")
+;;; * `' (out-pop #t) ;; now dump out the redirected output +]
+;;; * `' NULL @};
+;;; * @end example
+;;; *
+;;; * @noindent
+;;; * Some explanation:
+;;; *
+;;; * @noindent
+;;; * I added the @code{(out-push-new)} because the string table text is
+;;; * diverted into an output stream named, ``scribble'' and I want to
+;;; * have the string table emitted before the string table references.
+;;; * The string table references are also emitted inside the @code{FOR}
+;;; * loop. So, when the loop is done, the current output is suspended
+;;; * under the name, ``main'' and the ``scribble'' table is then emitted
+;;; * into the primary output. (@code{emit-string-table} inserts its
+;;; * output directly into the current output stream. It does not need to
+;;; * be the last function in an AutoGen macro block.) Next I
+;;; * @code{ag-fprintf} the array-of-pointer declaration directly into the
+;;; * current output. Finally I restore the ``main'' output stream and
+;;; * @code{(out-pop #t)}-it into the main output stream.
+;;; *
+;;; * Here is the result. Note that duplicate strings are not repeated
+;;; * in the string table:
+;;; *
+;;; * @example
+;;; * static char const scribble[18] =
+;;; * `' "that\0" "was\0" "the\0" "week\0";
+;;; *
+;;; * char const *ap[7] = @{
+;;; * `' scribble+0,
+;;; * `' scribble+5,
+;;; * `' scribble+9,
+;;; * `' scribble+13,
+;;; * `' scribble+0,
+;;; * `' scribble+5,
+;;; * `' NULL @};
+;;; * @end example
+;;; *
+;;; * These functions use the global name space @code{stt-*} in addition to
+;;; * the function names.
+;;; *
+;;; * If you utilize this in your programming, it is recommended that you
+;;; * prevent printf format usage warnings with the GCC option
+;;; * @code{-Wno-format-contains-nul}
+;;; =*/
+;;;
+(define string-table-new (lambda (st-name) (begin
+ (set! stt-curr (make-hash-table 31))
+ (hash-create-handle! stt-table st-name stt-curr)
+ (out-push-new)
+ (out-suspend st-name)
+ (set! stt-idx-tbl (make-hash-table 31))
+ (hash-create-handle! stt-curr "string-indexes" stt-idx-tbl)
+ (hash-create-handle! stt-curr "current-index" 0)
+ ""
+)))
+
+;;; /*=gfunc string_table_add
+;;; * general-use:
+;;; *
+;;; * what: Add an entry to a string table
+;;; *
+;;; * exparg: st-name , the name of the array of characters
+;;; * exparg: str-val , the (possibly) new value to add
+;;; *
+;;; * doc: Check for a duplicate string and, if none, then insert a new
+;;; * string into the string table. In all cases, returns the
+;;; * character index of the beginning of the string in the table.
+;;; *
+;;; * The returned index can be used in expressions like:
+;;; * @example
+;;; * string_array + <returned-value>
+;;; * @end example
+;;; * @noindent
+;;; * that will yield the address of the first byte of the inserted
+;;; * string. See the @file{strtable.test} AutoGen test for a usage
+;;; * example.
+;;; =*/
+;;;
+(define string-table-add (lambda (st-name str-val) (begin
+ (set! stt-curr (hash-ref stt-table st-name))
+ (set! stt-idx-tbl (hash-ref stt-curr "string-indexes"))
+ (set! stt-idx (hash-ref stt-idx-tbl str-val))
+ (if (not (number? stt-idx))
+ (begin
+ (set! stt-idx (hash-ref stt-curr "current-index"))
+ (ag-fprintf st-name "/* %5d */ %s \"\\0\"\n"
+ stt-idx (c-string str-val))
+ (hash-create-handle! stt-idx-tbl str-val stt-idx)
+ (hash-set! stt-curr "current-index"
+ (+ stt-idx (string-length str-val) 1) )
+ ) )
+ stt-idx
+)))
+
+;;; /*=gfunc string_table_add_ref
+;;; *
+;;; * what: Add an entry to a string table, get reference
+;;; * general-use:
+;;; *
+;;; * exparg: st-name , the name of the array of characters
+;;; * exparg: str-val , the (possibly) new value to add
+;;; *
+;;; * doc: Identical to string-table-add, except the value returned
+;;; * is the string "st-name" '+' and the index returned by
+;;; * string-table-add.
+;;; =*/
+;;;
+(define string-table-add-ref (lambda (st-name str-val)
+ (string-append st-name "+"
+ (number->string (string-table-add st-name str-val)) ) ))
+
+;;; /*=gfunc emit_string_table
+;;; *
+;;; * what: output a string table
+;;; *
+;;; * exparg: st-name , the name of the array of characters
+;;; *
+;;; * doc: Emit into the current output stream a
+;;; * @code{static char const} array named @code{st-name}
+;;; * that will have @code{NUL} bytes between each inserted string.
+;;; =*/
+;;;
+(define emit-string-table (lambda (st-name) (begin
+ (set! stt-curr (hash-ref stt-table st-name))
+ (set! stt-idx (hash-ref stt-curr "current-index"))
+ (ag-fprintf 0 "\nstatic char const %s[%d] =\n" st-name stt-idx)
+ (out-resume st-name)
+
+ ;; Columnize the output.
+ ;; Remove any leading spaces -- columns adds them itself.
+ ;; Glue the "\0" string to its preceding text.
+ ;; End the last line with a semi-colon
+ ;;
+ (emit (shell (string-append
+ "sed 's/^ / /
+ $s/\" \"\\\\0\"/\";/
+ s/\" \"\\\\0/\\\\0/
+ ' <<\\_EOF_\n"
+ (out-pop #t)
+ "_EOF_")))
+ (emit "\n")
+)))
+
+;;; /*=gfunc string_table_size
+;;; *
+;;; * what: print the current size of a string table
+;;; * general-use:
+;;; *
+;;; * exparg: st-name , the name of the array of characters
+;;; *
+;;; * doc: Returns the current byte count of the string table.
+;;; =*/
+;;;
+(define string-table-size (lambda (st-name)
+ (hash-ref (hash-ref stt-table st-name) "current-index") ))
+
+;;; /*=gfunc gperf_code
+;;; *
+;;; * what: emit the source of the generated gperf program
+;;; * general-use:
+;;; *
+;;; * exparg: st-name , the name of the gperf hash list
+;;; *
+;;; * doc:
+;;; * Returns the contents of the emitted code, suitable
+;;; * for inclusion in another program. The interface contains
+;;; * the following elements:
+;;; *
+;;; * @table @samp
+;;; * @item struct @i{<st-name>}_index
+;;; * containg the fields: @code{@{char const * name, int const id; @};}
+;;; *
+;;; * @item @i{<st-name>}_hash()
+;;; * This is the hashing function with local only scope (static).
+;;; *
+;;; * @item @i{<st-name>}_find()
+;;; * This is the searching and validation function. The first argument
+;;; * is the string to look up, the second is its length.
+;;; * It returns a pointer to the corresponding @code{@i{<st-name>}_index}
+;;; * entry.
+;;; * @end table
+;;; *
+;;; * Use this in your template as follows where "@i{<st-name>}" was
+;;; * set to be "@code{lookup}":
+;;; *
+;;; * @example
+;;; * [+ (make-gperf "lookup" (join "\n" (stack "name_list")))
+;;; * (gperf-code "lookup") +]
+;;; * void my_fun(char * str) @{
+;;; * struct lookup_index * li = lookup_find(str, strlen(str));
+;;; * if (li != NULL) printf("%s yields %d\n", str, li->idx);
+;;; * @end example
+;;; =*/
+;;;
+(define gperf-code (lambda (gp-name) (shellf
+ "sed -e '1,/^#line/d' \
+ -e '/#include/d' \
+ -e '/#line/d' \
+ -e '/^[ \t]*$/d' \
+ -e 's/^const struct /static const struct /' \
+ -e '/^int main(/,$d' ${gpdir}/%s.c"
+ gp-name
+)))
+
+;;; /*=gfunc stack_join
+;;; *
+;;; * what: stack values then join them
+;;; *
+;;; * exparg: join , string between each element
+;;; * exparg: ag-name , name of autogen values to stack
+;;; *
+;;; * doc: This function will collect all the values named @code{ag-name}
+;;; * (see the @pxref{SCM stack, stack function}) and join them
+;;; * separated by the @code{join} string (see the
+;;; * @pxref{SCM join, join function}).
+;;; =*/
+;;;
+(define stack-join (lambda (j-str ag-name)
+ (join j-str (stack ag-name))))
+
+;;; end of agen5/schemedef.scm
diff --git a/agen5/scmStrings.c b/agen5/scmStrings.c
new file mode 100644
index 0000000..1d9ac0b
--- /dev/null
+++ b/agen5/scmStrings.c
@@ -0,0 +1,203 @@
+
+/**
+ * @file scmStrings.c
+ *
+ * Temporary SCM strings.
+ *
+ * Time-stamp: "2012-08-11 08:29:47 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define ROUND_SCRIBBLE(_v, _sz) (((_v) + ((_sz) - 1)) & ~((_sz) - 1))
+
+typedef struct string_buf_s string_buf_t;
+
+struct string_buf_s {
+ string_buf_t * next_p;
+ ssize_t const sb_size;
+ ssize_t sb_off;
+ unsigned char sb_buf[1];
+};
+
+static string_buf_t * ag_strbufs = NULL;
+static string_buf_t ** next_strbuf = &ag_strbufs;
+static size_t const str_buf_hdr_sz =
+ (&(((string_buf_t *)NULL)->sb_buf[0])) - ((unsigned char *)NULL);
+
+/**
+ * Initialize the scribble string library
+ */
+LOCAL void
+ag_scribble_init(void)
+{
+ ag_strbufs = NULL;
+ next_strbuf = &ag_strbufs;
+}
+
+/**
+ * Free up the scribble strings memory pool.
+ */
+LOCAL void
+ag_scribble_deinit(void)
+{
+ string_buf_t* sb = ag_strbufs;
+ ag_scribble_init();
+
+ while (sb != NULL) {
+ string_buf_t* sb_next_p = sb->next_p;
+ free(sb);
+ sb = sb_next_p;
+ }
+}
+
+/**
+ * Free up the scribble strings used during the processing of one macro.
+ */
+LOCAL void
+ag_scribble_free(void)
+{
+ string_buf_t* sb = ag_strbufs;
+
+ while (sb != NULL) {
+ sb->sb_off = 0;
+ sb = sb->next_p;
+ }
+}
+
+/**
+ * allocate a new scribble block. Multiple of 8K bytes.
+ */
+static string_buf_t *
+new_scribble_block(size_t min_size)
+{
+ string_buf_t * res = NULL;
+
+ /*
+ * allow space for allocation header and round up
+ */
+ min_size += str_buf_hdr_sz;
+ min_size = ROUND_SCRIBBLE(min_size, 0x2000);
+
+ /*
+ * Allocate and link into list. Advance pointer to next entry.
+ */
+ *next_strbuf = res = AGALOC(min_size, "scribble buf");
+ next_strbuf = &(res->next_p);
+ res->next_p = NULL;
+ res->sb_off = 0;
+ /*
+ * The "sb_size" field is read-only. Override this.
+ */
+ {
+ size_t * psz = (void *)&(res->sb_size);
+ *psz = min_size - str_buf_hdr_sz;
+ }
+
+ return res;
+}
+
+/**
+ * Allocate a scribble string. It will be deallocated when a macro finishes.
+ * Therefore, *DO NOT* use these at the start of a block macro expecting
+ * the space to still be usable at the end of the block macro.
+ * These allocations are intended for temporary space needs that cannot
+ * be kept on the stack. Expression processing.
+ *
+ * @param size the number of bytes needed
+ * @return the memory as a pointer to character
+ */
+LOCAL char *
+ag_scribble(ssize_t size)
+{
+ string_buf_t* sb = ag_strbufs;
+ char* buf;
+
+ size += 1; // allow for NUL byte & round to word boundary
+ size = ROUND_SCRIBBLE(size, sizeof(void *));
+
+ for (;;) {
+ if (sb == NULL) {
+ sb = new_scribble_block(size);
+ break;
+ }
+
+ if ((sb->sb_size - sb->sb_off) >= size)
+ break;
+
+ sb = sb->next_p;
+ }
+
+ buf = (char*)(sb->sb_buf + sb->sb_off);
+ sb->sb_off += size;
+ return buf;
+}
+
+/**
+ * As of Guile 1.7.x, access to the NUL terminated string referenced by
+ * an SCM is no longer guaranteed. Therefore, we must extract the string
+ * into one of our "scribble" buffers.
+ *
+ * @param s the string to convert
+ * @param type a string describing the string
+ * @return a NUL terminated string, or it aborts.
+ */
+LOCAL char *
+ag_scm2zchars(SCM s, const char * type)
+{
+#if GUILE_VERSION < 107000 /* pre-Guile 1.7.x */
+
+ if (! AG_SCM_STRING_P(s))
+ AG_ABEND(aprf(NOT_STR_FMT, type));
+
+ if (SCM_SUBSTRP(s))
+ s = scm_makfromstr(SCM_CHARS(s), SCM_LENGTH(s), 0);
+ return SCM_CHARS(s);
+
+#else
+ size_t len;
+ char * buf;
+
+ if (! AG_SCM_STRING_P(s))
+ AG_ABEND(aprf(NOT_STR_FMT, type));
+
+ len = scm_c_string_length(s);
+ if (len == 0) {
+ static char z = NUL;
+ return &z;
+ }
+
+ buf = ag_scribble(len);
+
+ {
+ size_t buflen = scm_to_locale_stringbuf(s, buf, len);
+ if (buflen != len)
+ AG_ABEND(aprf(SCM2ZCHARS_BAD_VAL, buflen, len));
+ }
+
+ buf[len] = NUL;
+ return buf;
+#endif
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/scmStrings.c */
diff --git a/agen5/snarf.tpl b/agen5/snarf.tpl
new file mode 100644
index 0000000..e485628
--- /dev/null
+++ b/agen5/snarf.tpl
@@ -0,0 +1,316 @@
+[= AutoGen5 template -*- Mode: Text -*-
+
+# Time-stamp: "2011-01-31 14:16:48 bkorb"
+
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+(setenv "SHELL" "/bin/sh")
+
+ini =]
+[=#
+
+This template will emit the code necessary for registering callout routines
+for Guile/Scheme. The name of the output file will be ``basename.ini''
+where ``basename'' may be of your choosing.
+
+The following definitions are used:
+
+group A module prefix that preceeds the "scm_" prefix to all symbols
+init the name of the created initialization routine. This defaults
+ to "scm_init" or "group_init", if "group" is specified.
+ You must specify this for shared libraries.
+init-code code to put at the start of the init routine
+fini-code code to put at the end of the init routine
+
+gfunc this is a compound definition containing the following definitions
+ name the name of the function. The Scheme string name will normally be
+ derived from this name, but it may be over-ridden with the "string"
+ attribute. The transforming sed expression is:
+
+ sed -e's/_p$/?/;s/_x$/!/;s/_/-/g;s/-to-/->/'
+
+ string the Scheme name for the function, if not derivable from the name.
+ static If defined, then the function will not be exported.
+
+ exparg "EXPression ARGument" for each argument your routine handles,
+ you must specify one of these. This is a compound definition
+ with the following "attributes" that may be defined:
+
+ arg-name The name of the argument. required
+ arg-desc A very brief description of the argument. required
+ arg-optional Specify this if the argument is only optional
+ arg-list Specify this for the last argument if the last argument
+ may be an SCM list (i.e. an SCM-flavor of var args).
+
+syntax This defines a Guile syntax element. Read the Guile doc for
+ descriptions of the following attributes:
+ name the name of the C variable to hold the value
+ type
+ cfn
+ string the Scheme name for the syntax element, if not derivable.
+
+symbol This defines a Guile symbol.
+ name the name of the C variable to hold the value
+ init-val initial scm value for object
+ const-val initial integer value for object (signed long)
+ string the Scheme name for the symbol, if not derivable.
+
+If you are using a definitions file, these are defined in the normal
+way. If you are extracting them from `getdefs(1AG)' comments, then:
+
+1. `group' and `init' should be defined on the command line thus:
+ getdefs assign=group=XX assign=init=init_proc_name
+
+2. `init-code' and `fini-code' should be defined in a traditional
+ definitions file and be incorporated from a command line option:
+ getdefs copy=file-name.def
+
+3. `gfunc', `syntax', and `symbol' are getdefs' entry types.
+ The `name' attributes come from the getdefs entry name.
+ The remaining attributes are specified in the comment, per
+ the getdefs documentation.
+
+=][=
+
+(define ix 0)
+(define scm-prefix
+ (if (exist? "group")
+ (string-append (get "group") "_scm_")
+ "scm_" ))
+(out-push-new (string-append (base-name) ".h"))
+(dne " * " "/* ")=]
+ *
+ * copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+[=(gpl "AutoGen" " * ")=]
+ *
+ * Guile Implementation Routines[=% group " - for the %s group" =]
+ */
+[=(make-header-guard "GUILE_PROCS")=]
+#if GUILE_VERSION >= 108000
+# include <libguile.h>
+#else
+# include <guile/gh.h>
+#endif
+
+typedef enum {
+ GH_TYPE_UNDEFINED = 0,
+ GH_TYPE_BOOLEAN,
+ GH_TYPE_SYMBOL,
+ GH_TYPE_CHAR,
+ GH_TYPE_VECTOR,
+ GH_TYPE_PAIR,
+ GH_TYPE_NUMBER,
+ GH_TYPE_STRING,
+ GH_TYPE_PROCEDURE,
+ GH_TYPE_LIST,
+ GH_TYPE_INEXACT,
+ GH_TYPE_EXACT
+} teGuileType;
+[=
+FOR gfunc =]
+extern SCM [= (string-append scm-prefix (get "name") "(") =][=
+ IF (exist? "exparg") =][=
+ FOR exparg ", " =]SCM[=
+ ENDFOR =][=
+ ELSE =]void[=
+ ENDIF =]);[=
+ENDFOR gfunc =][=
+
+FOR symbol =][=
+ IF (exist? "global") =]
+extern SCM [= (string-append scm-prefix "sym_" (get "name") ";") =][=
+ ENDIF =][=
+ENDFOR symbol =]
+
+#endif /* [=(. header-guard)=] */
+[=
+
+(out-pop)
+(dne " * " "/* ")
+
+=]
+ *
+ * copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+[=
+(string-table-new "g_nm")
+
+(define add-to-g_nm (lambda ()
+ (string-table-add "g_nm"
+ (if (exist? "string")
+ (get "string")
+ (shellf
+ "echo '%s'|sed -e's/_p$/?/' -e's/_x$/!/' -e's/_/-/g' -e's/-to-/->/'"
+ (get "name") ) ) ) ))
+
+(gpl "AutoGen" " * ")=]
+ *
+ * Guile Initializations - [=% group (string-capitalize! "%s ")
+ =]Global Variables
+ */
+#include "[= (. header-file) =]"
+typedef SCM (*scm_callback_t)(void);
+void [=
+(define init-proc
+ (if (exist? "init")
+ (get "init")
+ (if (exist? "group")
+ (string-append (get "group") "_init")
+ "scm_init")))
+
+ init-proc =](void);
+[=
+
+ FOR symbol =][=
+ (sprintf "\n%s SCM %ssym_%-18s = SCM_BOOL_F;"
+ (if (exist? "global") "extern" "static")
+ scm-prefix (get "name") ) =][=
+ ENDFOR symbol
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */=][=
+
+IF (exist? "debug-enabled") =]
+#ifdef DEBUG_ENABLED[=
+
+ FOR gfunc
+
+=]
+static SCM
+agrelay_scm_[= (get "name") =]([=
+ IF (not (exist? "exparg")) =]void[=
+ (define pass-list "") =][=
+ ELSE =][=
+ (out-push-new) =][=
+ FOR exparg ", " =]SCM scm[= (for-index) =][= ENDFOR =][=
+ (define call-list (out-pop #t))
+ (define pass-list (shellf "echo '%s' | sed 's/SCM s/s/g'" call-list))
+ call-list =][=
+ ENDIF exparg exists/not =])
+{
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) {
+ static char const proc_z[] =
+ "Called ag_scm_[= name =]()\n";
+ fwrite(proc_z, sizeof(proc_z) - 1, 1, trace_fp);
+ }
+ return ag_scm_[= name =]([= (. pass-list) =]);
+}
+[= ENDFOR gfunc =]
+#if GUILE_VERSION >= 108000
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ scm_c_define_gsubr((char*)(_As), \
+ _Ar, _Ao, _Ax, (scm_callback_t)(void*)agrelay_scm_ ## _An)
+#else
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ gh_new_procedure((char*)(_As), (scm_callback_t)(void*)agrelay_scm_ ## _An, \
+ _Ar, _Ao, _Ax)
+#endif
+
+#else /* DEBUG_ENABLED *not* */[=
+
+ENDIF debug-enabled exists
+
+=]
+#if GUILE_VERSION >= 108000
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ scm_c_define_gsubr((char*)(_As), \
+ _Ar, _Ao, _Ax, (scm_callback_t)(void*)ag_scm_ ## _An)
+#else
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ gh_new_procedure((char*)(_As), (scm_callback_t)(void*)ag_scm_ ## _An, \
+ _Ar, _Ao, _Ax)
+#endif
+[= (if (exist? "debug-enabled") "#endif /* DEBUG_ENABLED */\n") =]
+/*
+ * [=group=] Initialization procedure.
+ */
+void
+[=(. init-proc)=](void)
+{[=
+
+ (out-push-new)
+
+ (if (exist? "init-code")
+ (prefix " " (get "init-code")) "") =][=
+
+ FOR gfunc =][=
+ INVOKE mk-new-proc =][=
+ ENDFOR gfunc =][=
+
+ FOR syntax =]
+ scm_make_synt(g_nm+[= (add-to-g_nm) =], [=type=], [=cfn=]);[=
+ ENDFOR syntax =][=
+
+ FOR symbol =]
+ [=(. scm-prefix)=]sym_[=name=] = scm_permanent_object[=
+ IF (not (and (exist? "init_val") (exist? "const_val")))
+ =](SCM_CAR (scm_intern0 (g_nm+[= (add-to-g_nm) =])));[=
+
+ ELSE =](scm_intern0 (g_nm+[= (add-to-g_nm) =]));
+ SCM_SETCDR ([=(. scm-prefix)=]sym_[=name=], [=
+ ?% init_val "%s" (sprintf "scm_long2num(%s)" (get "const_val"))=]);[=
+ ENDIF =][=
+ ENDFOR symbol =][=
+
+ (out-suspend "main")
+ (emit-string-table "g_nm")
+ (out-resume "main")
+ (out-pop #t) =][=
+ (if (exist? "fini-code")
+ (prefix " " (get "fini-code")) "") =]
+}
+#undef NEW_PROC
+/* end of [= (out-name) =] */
+[= #
+
+# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+=][=
+
+DEFINE mk-new-proc =][=
+
+ (set! ix (add-to-g_nm))
+
+ (ag-fprintf 0 "\n NEW_PROC(g_nm +%4d, " ix) =][=
+
+ IF (not (exist? "exparg"))
+
+ =]0, 0, 0[=
+
+ # Count of all the arguments: (count "exparg")
+ Of these, some may be optional: (count "exparg.arg_optional")
+ Of the optional, one may be an arg_list.
+ The sum of the three numbers must be: (count "exparg") =][=
+
+ ELIF (not (exist? "exparg.arg_list")) =][=
+ (- (count "exparg") (count "exparg.arg_optional")) =], [=
+ (count "exparg.arg_optional" ) =], 0[=
+
+ ELIF (not (exist? "exparg.arg_optional")) =][=
+ (- (count "exparg") 1) =], 0, 1[=
+
+ ELSE =][=
+ (- (count "exparg") (count "exparg.arg_optional")) =], [=
+ (- (count "exparg.arg_optional" ) 1) =], 1[=
+ ENDIF =], [=
+
+ name =]);[=
+
+ENDDEF =][=
+
+end of snarf.tpl \=]
diff --git a/agen5/test/Makefile.am b/agen5/test/Makefile.am
new file mode 100644
index 0000000..0915a4b
--- /dev/null
+++ b/agen5/test/Makefile.am
@@ -0,0 +1,71 @@
+## -*- Mode: Makefile -*-
+##
+## Makefile.am -- process this file with automake to produce Makefile.in
+##
+## Time-stamp: "2012-04-07 09:22:01 bkorb"
+## Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+SHELL_TESTS = define.test directives.test error.test expr.test extract.test \
+ include.test opts.test output.test snarf.test suffix.test shell.test
+
+NOSHELL_TESTS = \
+ alist.test case.test columns.test debug.test defref.test \
+ dynref.test endmac.test for.test forfrom.test forin.test \
+ format.test get.test gperf.test heredef.test html.test \
+ in.test leave.test license.test line.test loop.test \
+ make.test match.test pseudo.test reorder.test stack.test \
+ stress.test string.test strtable.test strxform.test time.test
+
+UNREADY_TESTS = daemon.test
+TESTS = @AGEN5_TESTS@
+EXTRA_DIST = $(TESTS) daemon.test
+FUNCLIST = funcCase.c funcDef.c funcEval.c funcFor.c funcIf.c functions.c
+EXPRLIST = expFormat.c expGuile.c expOutput.c expPrint.c expState.c \
+ expString.c expGperf.c expExtract.c $(FUNCLIST)
+
+TESTS_ENVIRONMENT = TERM='' \
+ CFLAGS='$(CFLAGS)' \
+ COMPILE='$(COMPILE)' \
+ EXPRLIST="$(EXPRLIST)" \
+ LDFLAGS='$(LDFLAGS)' \
+ SHELL="$(POSIX_SHELL)" \
+ srcdir="$(srcdir)" \
+ top_builddir="$(top_builddir)" \
+ top_srcdir="$(top_srcdir)"
+
+distclean-local:
+ -rm -rf testdir FAILURES
+
+$(TESTS) : defs
+
+defs : ${top_builddir}/autoopts/test/defs
+ @set -x ; \
+ $(SED) -e '/^ *srcdir=/s@".*"@"$(srcdir)"@' \
+ -e '/^ *top_srcdir=/s@".*"@"$(top_srcdir)"@' \
+ ${top_builddir}/autoopts/test/defs > $@ ; \
+ cd $(srcdir) ; chmod +x *.test || :
+
+${top_builddir}/autoopts/test/defs :
+ cd ${top_builddir}/autoopts/test ; $(MAKE) defs
+
+verbose : defs
+ rm -rf FAILURES testdir ; \
+ VERBOSE=true $(MAKE) check TESTS="$(TESTS)"
+
+# Makefile.am ends here
diff --git a/agen5/test/Makefile.in b/agen5/test/Makefile.in
new file mode 100644
index 0000000..61073be
--- /dev/null
+++ b/agen5/test/Makefile.in
@@ -0,0 +1,581 @@
+# Makefile.in generated by automake 1.12.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2012 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@
+am__make_dryrun = \
+ { \
+ am__dry=no; \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
+ | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
+ *) \
+ for am__flg in $$MAKEFLAGS; do \
+ case $$am__flg in \
+ *=*|--*) ;; \
+ *n*) am__dry=yes; break;; \
+ esac; \
+ done;; \
+ esac; \
+ test $$am__dry = yes; \
+ }
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = agen5/test
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/config/ag_macros.m4 \
+ $(top_srcdir)/config/extensions.m4 \
+ $(top_srcdir)/config/libopts.m4 \
+ $(top_srcdir)/config/libtool.m4 \
+ $(top_srcdir)/config/ltoptions.m4 \
+ $(top_srcdir)/config/ltsugar.m4 \
+ $(top_srcdir)/config/ltversion.m4 \
+ $(top_srcdir)/config/lt~obsolete.m4 \
+ $(top_srcdir)/config/onceonly.m4 \
+ $(top_srcdir)/config/snprintfv.m4 \
+ $(top_srcdir)/config/unlocked-io.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = $(am__tty_colors_dummy)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AGEN5_TESTS = @AGEN5_TESTS@
+AG_GUILE = @AG_GUILE@
+AG_LDFLAGS = @AG_LDFLAGS@
+AG_MAJOR_VERSION = @AG_MAJOR_VERSION@
+AG_MINOR_VERSION = @AG_MINOR_VERSION@
+AG_TIMEOUT = @AG_TIMEOUT@
+AG_VERSION = @AG_VERSION@
+AG_XML2 = @AG_XML2@
+AGexe = @AGexe@
+AGnam = @AGnam@
+AMTAR = @AMTAR@
+AO_AGE = @AO_AGE@
+AO_CURRENT = @AO_CURRENT@
+AO_REVISION = @AO_REVISION@
+AO_TEMPLATE_VERSION = @AO_TEMPLATE_VERSION@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CLexe = @CLexe@
+CLnam = @CLnam@
+CONFIG_SHELL = @CONFIG_SHELL@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEBUG_ENABLED = @DEBUG_ENABLED@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DYNAMIC_AG = @DYNAMIC_AG@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_STATIC = @ENABLE_STATIC@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GDexe = @GDexe@
+GDnam = @GDnam@
+GO_AGE = @GO_AGE@
+GO_CURRENT = @GO_CURRENT@
+GO_REVISION = @GO_REVISION@
+GREP = @GREP@
+GUILE_VERSION = @GUILE_VERSION@
+INCLIST = @INCLIST@
+INCSNPRINTFV = @INCSNPRINTFV@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBGUILE_CFLAGS = @LIBGUILE_CFLAGS@
+LIBGUILE_LIBS = @LIBGUILE_LIBS@
+LIBGUILE_PATH = @LIBGUILE_PATH@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSNPRINTFV = @LIBSNPRINTFV@
+LIBTOOL = @LIBTOOL@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIBXML2_PATH = @LIBXML2_PATH@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+M4_SRC = @M4_SRC@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPTS_TESTDIR = @OPTS_TESTDIR@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSIX_SHELL = @POSIX_SHELL@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TEXI2HTML = @TEXI2HTML@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_aux_dir = @ac_aux_dir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+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@
+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 = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SHELL_TESTS = define.test directives.test error.test expr.test extract.test \
+ include.test opts.test output.test snarf.test suffix.test shell.test
+
+NOSHELL_TESTS = \
+ alist.test case.test columns.test debug.test defref.test \
+ dynref.test endmac.test for.test forfrom.test forin.test \
+ format.test get.test gperf.test heredef.test html.test \
+ in.test leave.test license.test line.test loop.test \
+ make.test match.test pseudo.test reorder.test stack.test \
+ stress.test string.test strtable.test strxform.test time.test
+
+UNREADY_TESTS = daemon.test
+TESTS = @AGEN5_TESTS@
+EXTRA_DIST = $(TESTS) daemon.test
+FUNCLIST = funcCase.c funcDef.c funcEval.c funcFor.c funcIf.c functions.c
+EXPRLIST = expFormat.c expGuile.c expOutput.c expPrint.c expState.c \
+ expString.c expGperf.c expExtract.c $(FUNCLIST)
+
+TESTS_ENVIRONMENT = TERM='' \
+ CFLAGS='$(CFLAGS)' \
+ COMPILE='$(COMPILE)' \
+ EXPRLIST="$(EXPRLIST)" \
+ LDFLAGS='$(LDFLAGS)' \
+ SHELL="$(POSIX_SHELL)" \
+ srcdir="$(srcdir)" \
+ top_builddir="$(top_builddir)" \
+ top_srcdir="$(top_srcdir)"
+
+all: all-am
+
+.SUFFIXES:
+$(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) --gnu agen5/test/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu agen5/test/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+cscope cscopelist:
+
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile
+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:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-local
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: all all-am check check-TESTS check-am clean clean-generic \
+ clean-libtool distclean distclean-generic distclean-libtool \
+ distclean-local 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-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ uninstall uninstall-am
+
+
+distclean-local:
+ -rm -rf testdir FAILURES
+
+$(TESTS) : defs
+
+defs : ${top_builddir}/autoopts/test/defs
+ @set -x ; \
+ $(SED) -e '/^ *srcdir=/s@".*"@"$(srcdir)"@' \
+ -e '/^ *top_srcdir=/s@".*"@"$(top_srcdir)"@' \
+ ${top_builddir}/autoopts/test/defs > $@ ; \
+ cd $(srcdir) ; chmod +x *.test || :
+
+${top_builddir}/autoopts/test/defs :
+ cd ${top_builddir}/autoopts/test ; $(MAKE) defs
+
+verbose : defs
+ rm -rf FAILURES testdir ; \
+ VERBOSE=true $(MAKE) check TESTS="$(TESTS)"
+
+# Makefile.am ends here
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/agen5/test/alist.test b/agen5/test/alist.test
new file mode 100755
index 0000000..0f913fa
--- /dev/null
+++ b/agen5/test/alist.test
@@ -0,0 +1,79 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# alist.test --- test evalstack functionality
+#
+# Time-stamp: "2010-06-26 16:06:53 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+cat > $testname.tpl <<'_EOF_'
+[= AutoGen5 Template test =]
+[=
+
+FOR value "\n"
+
+=] [= (for-index) =] name is [=name=][=
+ENDFOR =]
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+cat > $testname.def <<'_EOF_'
+autogen definitions alist;
+
+\'(
+ (value ((name "first")) )
+ (value ((name "second") (name "third")) )
+ )
+
+value = { name = last; };
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo creating $testname.out
+# this is the output we should expect to see
+cat > $testname.out <<_EOF_
+ 0 name is first
+ 1 name is second
+ 2 name is last
+_EOF_
+
+AGCMD="-L ${srcdir}/.."
+run_ag x ${AGCMD} $testname.def || failure ${AGCMD} failed
+cmp -s $testname.test $testname.out || failure unexpected output
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of alist.test
diff --git a/agen5/test/case.test b/agen5/test/case.test
new file mode 100755
index 0000000..5ec29f9
--- /dev/null
+++ b/agen5/test/case.test
@@ -0,0 +1,136 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# case.test --- test CASE functionality
+#
+# Time-stamp: "2010-12-13 14:16:57 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+#
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+# # # # # # # TEMPLATE FILE # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<'EOF'
+<= AutoGen5 template test =>
+<=
+FOR grp "\n" =><=
+ CASE (get "nam") =><=
+
+ == first =>first: <=(sprintf "%-6s" (get "val"))=> matches: <=
+ CASE (get "val") =><=
+ ~~ ^[a-z]*$ =>~~ ^[a-z]*$ OK<=
+ ~~ ^[A-Z]*$ =>~~ ^[A-Z]*$ *** BOGUS ***<=
+ ~~ .* =>~~ .* *** BOGUS ***<=
+ ESAC => and <=
+ CASE (get "val") =><=
+ ~ ^[a-z]*$ =>~ ^[a-z]*$ OK<=
+ * =>~ .* *** BOGUS ***<=
+ ESAC =><=
+
+ == second =>second: <=(sprintf "%-6s" (get "val"))=> matches: <=
+ CASE (get "val") =><=
+ ~~ ^[a-z]*$ =>~~ ^[a-z]*$ *** BOGUS ***<=
+ ~~ ^[A-Z]*$ =>~~ ^[A-Z]*$ *** BOGUS ***<=
+ ~* . =>~* . OK<=
+ ESAC => and <=
+ CASE (get "val") =><=
+ ~ ^[a-z]*$ =>~ ^[a-z]*$ OK<=
+ * =>~ .* *** BOGUS ***<=
+ ESAC =><=
+
+ == third =>third: <=(sprintf "%-6s" (get "val"))=> matches: <=
+ CASE (get "val") =><=
+ ~~ ^[a-z]*$ =>~~ ^[a-z]*$ *** BOGUS ***<=
+ ~~ ^[A-Z]*$ =>~~ ^[A-Z]*$ OK<=
+ ~~ .* =>~~ .* *** BOGUS ***<=
+ ESAC => and <=
+ CASE (get "val") =><=
+ ~ ^[a-z]*$ =>~ ^[a-z]*$ OK<=
+ * =>~ .* *** BOGUS ***<=
+ ESAC =><=
+
+ ESAC =><=
+
+ENDFOR grp=>
+empty: <= CASE empty-val =><=
+ ~* . =>WRONG<=
+ !E =>BOGUS<=
+ +E =>valid<=
+ * =>REAL-BOGUS<=
+ ESAC =>
+bogus: <= CASE bogus-val =><=
+ ~* . =>WRONG<=
+ +E =>BOGUS<=
+ !E =>valid<=
+ * =>REAL-BOGUS<=
+ ESAC =>
+EOF
+
+# # # # # # # SAMPLE OUTPUT FILE # # # # # #
+
+echo creating ${testname}.out in `pwd`
+# this is the output we should expect to see
+cat > ${testname}.out <<'EOF'
+first: string matches: ~~ ^[a-z]*$ OK and ~ ^[a-z]*$ OK
+second: String matches: ~* . OK and ~ ^[a-z]*$ OK
+third: STRING matches: ~~ ^[A-Z]*$ OK and ~ ^[a-z]*$ OK
+empty: valid
+bogus: valid
+EOF
+
+# # # # # # # DEFINITIONS FILE # # # # # #
+
+cat > ${testname}.def <<EOF
+autogen definitions ${testname};
+
+grp = {
+ nam = first;
+ val = string;
+};
+
+grp = {
+ nam = second;
+ val = String;
+};
+
+grp = {
+ nam = third;
+ val = STRING;
+};
+empty-val;
+EOF
+
+# # # # # # # RUN AUTOGEN # # # # # #
+
+run_ag x ${testname}.def || failure autogen failed
+
+# # # # # # # TEST RESULTS # # # # # #
+
+cmp -s ${testname}.test ${testname}.out || failure unexpected output
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of case.test
diff --git a/agen5/test/columns.test b/agen5/test/columns.test
new file mode 100755
index 0000000..6c02f09
--- /dev/null
+++ b/agen5/test/columns.test
@@ -0,0 +1,56 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# case.test --- test CASE functionality
+#
+# Time-stamp: "2010-12-13 14:24:49 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+#
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+# # # # # # # # # # # # # TEST 1 # # # # # # # # # # # #
+
+${CLexe:-columns} --first '#define TABLE' --spread=1 --line=' \' \
+ -I4 -c 3 -f '_Tbl_(%s)'> ${testname}.res <<_EOF_
+one
+two
+three
+four
+five
+six
+_EOF_
+
+cat > ${testname}.base <<\_EOF_
+#define TABLE \
+ _Tbl_(one) _Tbl_(two) _Tbl_(three) \
+ _Tbl_(four) _Tbl_(five) _Tbl_(six)
+_EOF_
+
+cmp ${testname}.base ${testname}.res || \
+ failure "malformed result:${nl}`diff ${testname}.base ${testname}.res`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of columns.test
diff --git a/agen5/test/daemon.test b/agen5/test/daemon.test
new file mode 100755
index 0000000..33114e7
--- /dev/null
+++ b/agen5/test/daemon.test
@@ -0,0 +1,127 @@
+#! /bin/ksh
+# -*- Mode: Shell-script -*-
+# daemon.test --- test functionality of `for' function
+#
+# Time-stamp: "2011-02-02 12:00:31 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+tdir=$( (mktemp -d $PWD/.daemon-XXXXXX.d) 2>/dev/null)
+test -z "${tdir}" && {
+ tdir=$PWD/.daemon-$$.d
+ rm -rf ${tdir}
+ mkdir ${tdir} || exit 1
+}
+
+ag=`cd .. >/dev/null ; pwd`/autogen
+
+case $- in
+*x* ) setcmd="set -ex" ;;
+* ) setcmd='set -e' ;;
+esac
+cd ${tdir}
+
+exec 5> daemon-results
+
+cat > daemon.tpl <<_EOF_
+[= AutoGen5 Template =]
+This is from the [= (shell "echo $$") =] server.
+_EOF_
+
+run_thrice() {
+ $setcmd
+ sleep 1
+ for f in 1 2 3
+ do (
+ cat > ${1} <<_EOF_
+AutoGen Definitions daemon.tpl;
+hello = yes;
+_EOF_
+
+ sleep 1
+
+ cat >&5 < ${2}
+ ) &
+ done
+ sleep 1
+}
+
+do_run() {
+ $setcmd
+ $cmd &
+ pid=$!
+
+ while test ! -S ${1} -a ! -p ${1}
+ do print -u2 No ${1}. Must delay.
+ ( ps -p ${pid} > /dev/null 2>&1 ) || {
+ print -u2 "autogen has gone away"
+ exit 1
+ }
+ sleep 1
+ done
+
+ run_thrice "$@"
+ ps -fp $pid
+
+ kill -HUP ${pid} || \
+ kill -TERM ${pid} || \
+ kill -KILL ${pid}
+}
+
+( ${ag} --daemon daemon-io --help > /dev/null 2>&1 ) || exit 0
+
+cmd="${ag} --daemon daemon-io -b dmn"
+do_run daemon-io-in daemon-io-out
+
+tmp=$(echo ${tdir} | ${SED} 's,.*/\.,,;s/\.d$//')
+pipe=$PWD/${tmp}
+
+cmd="${ag} --daemon unix:${pipe} -b dmn"
+do_run ${tmp} ${tmp}
+
+sleep 2
+exec 5>&-
+cat daemon-results
+cd ..
+rm -rf ${tdir}
+
+# No daemon-io-in. Must delay.
+# UID PID PPID C STIME TTY TIME CMD
+# bkorb 18431 18427 3 18:18 pts/1 00:00:00 /home/bkorb/ag/ag/agen5/.libs/lt
+# AutoGen aborting on signal 1 (Hangup) in state OPTIONS
+# No daemon-w0QjvL. Must delay.
+# ./daemon.test[71]: cannot create daemon-w0QjvL: No such device or address
+# ./daemon.test[71]: cannot create daemon-w0QjvL: No such device or address
+# ./daemon.test[71]: cannot create daemon-w0QjvL: No such device or address
+# UID PID PPID C STIME TTY TIME CMD
+# bkorb 18466 18427 3 18:18 pts/1 00:00:00 /home/bkorb/ag/ag/agen5/.libs/lt
+# AutoGen aborting on signal 1 (Hangup) in state OPTIONS
+
+# This is from the 18427 server.
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of daemon.test
diff --git a/agen5/test/debug.test b/agen5/test/debug.test
new file mode 100755
index 0000000..d83216f
--- /dev/null
+++ b/agen5/test/debug.test
@@ -0,0 +1,175 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# debug.test --- definition reference testing
+#
+# Time-stamp: "2012-04-07 09:45:03 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+#
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+cat > ${testname}.tpl <<_EOF_
+<= AutoGen5 template test =>
+The last of each list:<=
+FOR list =><= DEBUG slot 64 =>
+ LAST -> <= val[$] =>
+
+ list -> <=
+ FOR val ", " =><= DEBUG bucket 128 => val[<=(for-index)=>]=<=val=><=
+ ENDFOR =><=
+ENDFOR =>
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+# Create the files we need in the test environment
+cat > ${testname}.def <<_EOF_
+AutoGen definitions "${testname}." "tpl";
+list = { val = L0.first, L0.last; };
+List[3] = { val = L3.third.all; };
+_EOF_
+
+# # # # # # # # # # ANTICIPATED RESULTS # # # # # # # # #
+
+cat > ${testname}.base1 <<\_EOF_
+
+eval from file agInit.c line XXX:
+(debug-enable 'backtrace)
+Definition Load:
+list[0] (block) from debug.def/2
+ val[0] (text) from debug.def/2
+ val[1] (text) from debug.def/2
+list[3] (block) from debug.def/3
+ val[0] (text) from debug.def/3
+marker ``<='' loaded
+marker ``=>'' loaded
+Starting test template
+open_output_file 'debug.test' mode wb+
+Text (15) in debug.tpl at line 2
+ The last of each list:
+FOR ( F) in debug.tpl at line 3
+FOR list loop in debug.tpl on line 3 begins:
+DEBUG ( 5) in debug.tpl at line 3
+ slot 64
+ -- DEBUG slot 64 -- FOR index 0
+Text (15) in debug.tpl at line 3
+
+Unknown (16) in debug.tpl at line 4
+remapped to 'Expr' (16) in debug.tpl at line 4
+ based on val[$]
+Text (15) in debug.tpl at line 4
+
+FOR ( F) in debug.tpl at line 7
+ ,
+FOR val loop in debug.tpl on line 7 begins:
+DEBUG ( 5) in debug.tpl at line 7
+ bucket 128
+ -- DEBUG bucket 128 -- FOR index 0
+Text (15) in debug.tpl at line 7
+ val[
+EXPR ( E) in debug.tpl at line 7
+ (for-index)
+eval from file debug.tpl line 7:
+(for-index)
+Text (15) in debug.tpl at line 7
+ ]=
+Unknown (16) in debug.tpl at line 7
+remapped to 'Expr' (16) in debug.tpl at line 7
+ based on val
+DEBUG ( 5) in debug.tpl at line 7
+ bucket 128
+ -- DEBUG bucket 128 -- FOR index 1
+Text (15) in debug.tpl at line 7
+ val[
+EXPR ( E) in debug.tpl at line 7
+ (for-index)
+eval from file debug.tpl line 7:
+(for-index)
+Text (15) in debug.tpl at line 7
+ ]=
+EXPR ( E) in debug.tpl at line 7
+FOR val repeated 2 times
+ from debug.tpl line 7
+DEBUG ( 5) in debug.tpl at line 3
+ slot 64
+ -- DEBUG slot 64 -- FOR index 3
+Text (15) in debug.tpl at line 3
+
+EXPR ( E) in debug.tpl at line 4
+Text (15) in debug.tpl at line 4
+
+FOR ( F) in debug.tpl at line 7
+ ,
+FOR val loop in debug.tpl on line 7 begins:
+DEBUG ( 5) in debug.tpl at line 7
+ bucket 128
+ -- DEBUG bucket 128 -- FOR index 0
+Text (15) in debug.tpl at line 7
+ val[
+EXPR ( E) in debug.tpl at line 7
+ (for-index)
+eval from file debug.tpl line 7:
+(for-index)
+Text (15) in debug.tpl at line 7
+ ]=
+EXPR ( E) in debug.tpl at line 7
+FOR val repeated 1 times
+ from debug.tpl line 7
+FOR list repeated 2 times
+ from debug.tpl line 3
+Text (15) in debug.tpl at line 9
+
+out_close 'debug.test'
+_EOF_
+
+traceout=${testname}-aglog-x1-$$.log
+
+if ${VERBOSE}
+then trace_args=''
+else trace_args=--trace=everything\ --trace-out=${traceout}
+fi
+
+run_ag x1 ${trace_args} ${testname}.def || failure AutoGen failed
+${SED} -e 's@from file .*agInit.*@from file agInit.c line XXX:@' \
+ -e '/Called ag_scm_for_index/d' \
+ -e '/^AutoGen [0-9]* starts:/d' \
+ ${traceout} > ${testname}.trace1
+cmp -s ${testname}.base1 ${testname}.trace1 || \
+ failure "`diff -c ${testname}.base1 ${testname}.trace1`"
+
+${VERBOSE} || {
+ traceout=${testname}-aglog-x2-$$.log
+ trace_args=--trace-out=${traceout}
+ run_ag x2 ${trace_args} ${testname}.def || failure AutoGen failed
+ test -f ${traceout} -a -s ${traceout} && \
+ failure "autogen produced trace output"
+}
+
+cleanup
+
+##
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of debug.test
diff --git a/agen5/test/define.test b/agen5/test/define.test
new file mode 100755
index 0000000..68008e4
--- /dev/null
+++ b/agen5/test/define.test
@@ -0,0 +1,160 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# define.test --- test evalstack functionality
+#
+# Time-stamp: "2012-04-07 09:40:07 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # TEMPLATE LIBRARY FILE # # # # # #
+
+echo creating $testname.tlib
+cat > $testname.tlib <<'_EOF_'
+[+ AutoGen5 Template Library +]
+[+
+
+# * * * * * * * * * * * *
+
+ VISUAL SEPARATION
++][+
+
+DEFINE first_macro
++][+(for-index)+] Argument list:
+ [+
+ FOR arg "\n\t"
+ +]arg[ [+(for-index)+] ] = [+ arg +][+
+ ENDFOR +][+
+ENDDEF +][+
+
+# * * * * * * * * * * * *
+
+ VISUAL SEPARATION
++][+
+
+DEFINE last_macro
+ +]This is the last (number [+(for-index)+]) macro.[+
+ENDDEF +]
+_EOF_
+
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+cat > $testname.tpl <<'_EOF_'
+[= AutoGen5 Template test =]
+[=
+
+DEFINE nest =][=
+ IF (exist? "name") =]
+level [=level=] iteration [=(for-index)=] Nested Name: [=name=][=
+ ENDIF =][=
+
+ FOR .nesting =][=
+ nest =][=
+ ENDFOR =][=
+
+ENDDEF nest =][=
+
+FOR value
+
+=][=
+ IF (or (first-for?) (last-for?)) =][=
+ INVOKE (string-append (get "value") "_macro")
+ arg = one arg = "the second arg = the second"
+ arg = (shell "date +'%A, %D'") arg = 'done.' =][=
+ ELSE =]
+
+ non first/last value: [=value=] = [= (for-index) =]
+[=ENDIF =][=
+ENDFOR =]
+[=invoke nest =]
+Done.
+_EOF_
+
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+cat > $testname.def <<_EOF_
+autogen definitions $testname;
+
+value = first;
+value[2] = secondary;
+value[4] = tertiary;
+value[6] = last;
+
+level = 0;
+nesting[1] = {
+ level = 1;
+ name = primary;
+ nesting[1] = {
+ level = 2;
+ name = secondary;
+ }, {
+ level = 2;
+ /* no name defined - default to primary level */
+ nesting[1] = {
+ level = 3;
+ name = tertiary;
+ };
+ };
+};
+_EOF_
+
+# this is the output we should expect to see
+cat > $testname.samp <<_EOF_
+0 Argument list:
+ arg[ 0 ] = one
+ arg[ 1 ] = the second arg = the second
+ arg[ 2 ] = `date +'%A, %D'`
+ arg[ 3 ] = done.
+
+ non first/last value: secondary = 2
+
+
+ non first/last value: tertiary = 4
+This is the last (number 6) macro.
+
+level 1 iteration 1 Nested Name: primary
+level 2 iteration 1 Nested Name: secondary
+level 2 iteration 2 Nested Name: primary
+level 3 iteration 1 Nested Name: tertiary
+Done.
+_EOF_
+
+run_ag x --lib=$testname.tlib $testname.def || \
+ failure "autogen failed"
+set -x
+cmp -s $testname.samp $testname.test || \
+ failure "`diff $testname.samp $testname.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of define.test
diff --git a/agen5/test/defref.test b/agen5/test/defref.test
new file mode 100755
index 0000000..cac27d5
--- /dev/null
+++ b/agen5/test/defref.test
@@ -0,0 +1,124 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# defref.test --- definition reference testing
+#
+# Time-stamp: "2011-02-02 12:09:02 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+${SED} 's/^ *[0-9]*://;s/^ //' > ${testname}.tpl <<_EOF_
+ 1: <= AutoGen5 template test =>
+ 2: The last of each list:<=
+ 3: FOR list =>
+ 4: list[<= (for-index) =>] -> <= val[$] =><=
+ 5: ENDFOR =>
+ 6:
+ 7: The first of each list:<=
+ 8: FOR list =>
+ 9: list[<=(for-index)=>] -> <=val[]=><=
+10: ENDFOR =>
+11:
+12: The full list:<=
+13: FOR list =>
+14: list[<=(for-index)=>] -> <=
+15: FOR val ", " => val[<=(for-index)=>]=<=val=><=
+16: ENDFOR =><=
+17: ENDFOR =>
+18:
+19: The local global list:<=
+20: FOR list =>
+21: list[<=(for-index)=>] == "<=
+22: (join ", " (stack ".param.name")) =>"
+21: .. "<=
+22: (join ", " (stack "param.name")) =>"<=
+23: ENDFOR =>
+24:
+25: The first value is: <= ?% list[].val[] "val = %s (first)" "Undefined??"=>
+26: The last value is: <= ?% list[$].val[$] "val = %s (last)" "Undefined??"=>
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+# Create the files we need in the test environment
+cat > ${testname}.def <<_EOF_
+AutoGen definitions "${testname}." "tpl";
+param = { name = "global"; };
+list = { val = L0.first, L0.second, L0.last;
+ param = { name = "first-1", "first-2"; }; },
+ { val[2] = L1.second-and-last; Val[0] = L1.first;
+ param = { name = "second-1", "second-2"; }; };
+List[3] = { val = L3.third.all; },
+ { val[3] = L4.last; VAL[2] = L4.middle; VAL[1] = L4.first; };
+_EOF_
+
+# # # # # # # # # # ANTICIPATED RESULT # # # # # # # # #
+
+cat > ${testname}.out <<_EOF_
+The last of each list:
+ list[0] -> L0.last
+ list[1] -> L1.second-and-last
+ list[3] -> L3.third.all
+ list[4] -> L4.last
+
+The first of each list:
+ list[0] -> L0.first
+ list[1] -> L1.first
+ list[3] -> L3.third.all
+ list[4] -> L4.first
+
+The full list:
+ list[0] -> val[0]=L0.first, val[1]=L0.second, val[2]=L0.last
+ list[1] -> val[0]=L1.first, val[2]=L1.second-and-last
+ list[3] -> val[0]=L3.third.all
+ list[4] -> val[1]=L4.first, val[2]=L4.middle, val[3]=L4.last
+
+The local global list:
+ list[0] == "first-1, first-2"
+ .. "first-1, first-2"
+ list[1] == "second-1, second-2"
+ .. "second-1, second-2"
+ list[3] == ""
+ .. "global"
+ list[4] == ""
+ .. "global"
+
+The first value is: val = L0.first (first)
+The last value is: val = L4.last (last)
+_EOF_
+
+run_ag x ${testname}.def || failure AutoGen failed
+cmp -s ${testname}.test ${testname}.out || \
+ failure "`diff ${testname}.test ${testname}.out`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of defref.test
diff --git a/agen5/test/directives.test b/agen5/test/directives.test
new file mode 100755
index 0000000..aab6833
--- /dev/null
+++ b/agen5/test/directives.test
@@ -0,0 +1,148 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# directives.test --- test definition directives
+#
+# Time-stamp: "2011-02-02 12:09:10 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+mkdir ${testname}.dir
+mkdir ${testname}.dir/subdir
+LDIR=`pwd`/${testname}.dir
+export LDIR
+
+echo creating ${testname}.dir/subdir/${testname}.tpl
+cat > ${testname}.dir/subdir/${testname}.tpl <<'_EOF_'
+[= AutoGen5 Template test =]
+Irrelevant [=foo=].
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating ${testname}.inc
+${SED} 's/^ *[0-9]*: //' > ${testname}.inc <<_EOF_
+ 1: autogen definitions ${testname};
+ 2:
+ 3: #option templ-dir \$LDIR/subdir
+ 4:
+ 5: /*
+ 6: #define COMMENT
+ 7: #ifdef COMMENT
+ 8: # error error in comment
+ 9: */
+ 10:
+ 11: #ifdef MUMBLE
+ 12: # define MORE
+ 13: #else
+ 14: # define FUMBLE
+ 15: #endif
+_EOF_
+
+echo creating ${testname}.def
+${SED} 's/^ *[0-9]*: //' > ${testname}.def <<_EOF_
+ 1: autogen definitions ${testname};
+ 2:
+ 3: #include ${testname}.inc
+ 4:
+ 5: #ifdef FUMBLE
+ 6: # error we fumbled mumble
+ 7: #endif
+ 8:
+ 9: #ifndef MORE
+ 10: # error we fumbled more
+ 11: #endif
+ 12:
+ 13: #if (FOO == 1)
+ 14: # error we got iffed
+ 15: #elif (FOO == 0)
+ 16: # error we got FOO zeroed
+ 17: #else
+ 18: # error we got lost
+ 19: #endif
+ 20:
+ 21: #shell
+ 22:
+ 23: echo "#line 1 shelltext"
+ 24: echo "foo = bar;"
+ 25:
+ 26: #endshell
+ 27:
+ 28: #define FUMBLE
+ 29: #undef MORE
+ 30:
+ 31: #ifndef FUMBLE
+ 32: # error we goofed fumble
+ 33: #endif
+ 34:
+ 35: #ifdef MORE
+ 36: # error we mumbled more
+ 37: #endif
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo creating ${testname}.base
+# this is the output we should expect to see
+cat > ${testname}.base <<_EOF_
+Irrelevant bar.
+_EOF_
+
+agopts="-DMUMBLE -DFOO=1"
+MORE=DOITWRONG
+
+run_ag x1 ${agopts} ${testname}.def
+test $? -eq 0 || failure ${agopts}-1 failed
+cmp -s ${testname}.base ${testname}.test || \
+ failure "`diff ${testname}.base ${testname}.test`"
+
+echo '#assert `echo true`' >> ${testname}.inc
+run_ag x2 ${agopts} ${testname}.def
+test $? -eq 0 || failure ${agopts}-2 failed
+
+echo '#assert `echo false`' >> ${testname}.inc
+run_ag x3 ${agopts} ${testname}.def
+test $? -ne 0 || failure ${agopts}-3 failed
+
+${FGREP} -v '#assert' ${testname}.inc > ${testname}2.inc
+mv -f ${testname}2.inc ${testname}.inc
+
+echo '#assert (version-compare > autogen-version "5.8.4")' >> ${testname}.inc
+run_ag x4 ${agopts} ${testname}.def
+test $? -eq 0 || failure ${agopts}-4 failed
+
+echo '#assert (version-compare < autogen-version "5.8.4.63")' >> ${testname}.inc
+run_ag x5 ${agopts} ${testname}.def
+test $? -ne 0 || failure ${agopts}-5 failed
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of directives.test
diff --git a/agen5/test/dynref.test b/agen5/test/dynref.test
new file mode 100755
index 0000000..1f62248
--- /dev/null
+++ b/agen5/test/dynref.test
@@ -0,0 +1,82 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# dynref.test --- test dynamic scoping of definition values
+#
+# Time-stamp: "2010-06-26 16:06:18 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+cat > $testname.tpl <<'_EOF_'
+[= AutoGen5 Template test =]
+[=
+
+DEFINE nest =][=
+
+ FOR nesting =][=
+ IF (first-for?)
+=]The un-nested value is: [=val_u=].[=
+ ENDIF =][=
+ ENDFOR =][=
+
+ENDDEF =][=
+
+nest Val-u = "hidden value"
+
+=]
+_EOF_
+
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+cat > $testname.def <<_EOF_
+autogen definitions $testname;
+
+nesting = { name = one; };
+nesting = { name = two; };
+nesting = { name = three; };
+nesting = { name = four; };
+_EOF_
+
+# this is the output we should expect to see
+cat > $testname.out <<_EOF_
+The un-nested value is: hidden value.
+_EOF_
+
+run_ag x $testname.def || failure autogen failed
+cmp -s $testname.test $testname.out || \
+ failure "`diff $testname.test $testname.out`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of dynref.test
diff --git a/agen5/test/endmac.test b/agen5/test/endmac.test
new file mode 100755
index 0000000..c5ac062
--- /dev/null
+++ b/agen5/test/endmac.test
@@ -0,0 +1,73 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# endmac.test --- test variations on the end macro marker
+#
+# Time-stamp: "2011-02-24 10:53:51 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+set -x
+cat > ${testname}1.tpl <<EOF
+<=AutoGen5 template test=>><<=
+`echo ENDMAC 1`
+=>> <=`pwd`=>>
+<=# comment this test fails because the '=' is bound to 'test' >>
+This is a test
+EOF
+
+run_ag x1 -T${testname}1 --no-def 2> /dev/null && \
+ failure $testname failure test was successful. Oops.
+echo $testname failure test failed correctly
+
+cat > ${testname}2.tpl <<EOF
+<=AutoGen5 template test =>>
+ENDMAC 2 <=`echo print this message` =>>
+<=# comment This test succeeds because the '=' is not part of 'test' =>>
+This is a test
+EOF
+
+run_ag x2 -T${testname}2 --no-def || \
+ failure $testname success test was a failure
+echo first successful $testname test succeeded
+
+cat > ${testname}3.tpl <<EOF
+<+AutoGen5 template test+>>
+ENDMAC 3 <+(define foo "print this message") foo +>>
+<+# comment The '+' is not joined because it is neither '=' nor
+ a legal file name character +>>
+This is a test
+EOF
+
+run_ag x3 -T${testname}3 --no-def || \
+ failure second $testname success test was a failure
+echo second successful $testname test succeeded
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of endmac.test
diff --git a/agen5/test/error.test b/agen5/test/error.test
new file mode 100755
index 0000000..de9a896
--- /dev/null
+++ b/agen5/test/error.test
@@ -0,0 +1,250 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+#
+# error.test --- test error functionality
+#
+# Author: Bruce Korb <bkorb@gnu.org>
+# Time-stamp: "2011-12-30 16:40:11 bkorb"
+#
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+verb_ok=true
+
+ag_err()
+{
+ (
+ set +x
+ if ${VERBOSE} && ${verb_ok}
+ then
+ AUTOGEN_TRACE=everything
+ AUTOGEN_TRACE_OUT=${testname}-task${seq}-aglog.txt
+ VERBOSE=false
+ export AUTOGEN_TRACE AUTOGEN_TRACE_OUT
+ else
+ unset AUTOGEN_TRACE AUTOGEN_TRACE_OUT
+ fi
+
+ seq=${1} ; shift
+ exec 2> ${testname}.err${seq}
+ touch ${testname}-task${seq}-running
+ run_ag x${seq} "$@"
+ res=$?
+ rm -f ${testname}-task${seq}-running
+ exit $res
+ ) && touch ${testname}-task${seq}-OK
+}
+
+# # # # # # # # # # # # # TEST 1 # # # # # # # # # # # #
+
+echo creating ${testname}.tpl
+${SED} 's/[ 0-9]*=//' > ${testname}.tpl <<'_EOF_'
+ 1 =<= AutoGen5 template fnord null =>
+ 2 =THIS TEXT SHOULD BE DELETED!!
+ 3 =<=
+ 4 =CASE (suffix) =><=
+ 5 =
+ 6 = == fnord =><=
+ 7 =
+ 8 = ;; (out-push-new)
+ 9 = ;; (error (string-append "0 this is really okay" (out-pop #t))
+ 10 = (error "0 this is really okay")
+ 11 =
+ 12 = =>this is never seen<=
+ 13 =
+ 14 = * =><=
+ 15 =
+ 16 = (error "This is a multi-line
+ 17 = error to show how to exit
+ 18 = your template") =><=
+ 19 =
+ 20 =ESAC =>
+ 21 =this is never seen
+_EOF_
+
+echo creating ${testname}.def
+cat > ${testname}.def <<'_EOF_'
+autogen definitions alist;
+#error The definitions file has a `#error' directive
+_EOF_
+
+echo creating ${testname}.base1
+# this is the output we should expect to see
+cat > ${testname}.base1 <<'_EOF_'
+#error directive -- in error.def on line 3
+ The definitions file has a `#error' directive
+_EOF_
+
+ag_err 1 ${testname}.def
+test -f ${testname}-task1-OK \
+ && failure unexpected success for test-1
+
+${SED} '/Giving up in/d;/Failing Guile command:/,$d' ${testname}.err1 \
+ > ${testname}.err
+cmp -s ${testname}.err ${testname}.base1 || \
+ failure "expected->actual${nl}`diff -c ${testname}.base1 ${testname}.err`"
+
+# # # # # # # # # # # # # TEST 2 # # # # # # # # # # # #
+
+echo creating ${testname}.base2
+# this is the output we should expect to see
+cat > ${testname}.base2 <<'_EOF_'
+DEFINITIONS Warning in error.tpl line 8 for error2.fnord:
+ 0 this is really okay
+Error in template error.tpl, line 16
+ DEFINITIONS ERROR in error.tpl line 16 for /dev/null:
+ This is a multi-line
+ error to show how to exit
+ your template
+Failing Guile command: = = = = =
+
+(error "This is a multi-line
+ error to show how to exit
+ your template")
+
+=================================
+_EOF_
+
+agopts="--no-def --base=${testname}"
+
+verb_ok=false
+ag_err 2 -T${testname}.tpl ${agopts}2
+test -f ${testname}-task2-OK \
+ && failure unexpected success for test-2
+verb_ok=true
+
+if test ${GUILE_VERSION} -gt 107000
+then
+ ${EGREP} -v 'Giving up in | is deprecated\.' ${testname}.err2
+else
+ ${EGREP} -v 'Giving up in ' ${testname}.err2
+fi > ${testname}.res2
+
+cmp -s ${testname}.base2 ${testname}.res2 || \
+ failure "expected->actual${nl}`diff -c ${testname}.base2 ${testname}.res2`"
+
+# # # # # # # # # # # # # TEST 3 # # # # # # # # # # # #
+
+# REMOVED: This test always worked, but the exact results
+# vary all over the place. Too hard to test.
+
+# # # # # # # # # # # # # TEST 4 # # # # # # # # # # # #
+
+${SED} 's,^#error.*,this is broken;,' ${testname}.def > ${testname}2.def
+
+echo creating ${testname}.base4
+# this is the output we should expect to see
+cat > ${testname}.base4 <<'_EOF_'
+FSM Error: in state 5 (have_name), event 3 (var_name) is invalid
+invalid transition: in error2.def on line 2
+ token in error: var_name: ''is broken;
+''
+
+ [[...<error-text>]] broken;
+
+
+Likely causes: a mismatched quote, a value that needs quoting,
+ or a missing semi-colon
+_EOF_
+
+ag_err 4 ${testname}2.def
+test -f ${testname}-task4-OK \
+ && failure unexpected success for test-4
+
+${GREP} -v 'Giving up in' ${testname}.err4 > ${testname}.res4
+cmp -s ${testname}.res4 ${testname}.base4 \
+ || failure "expected->actual${nl}`diff -c ${testname}.base4 ${testname}.res4`"
+
+# # # # # # # # # # # # # TEST 5 # # # # # # # # # # # #
+
+echo creating ${testname}.tpl5
+cat > ${testname}.tpl5 <<'_EOF_'
+<= AutoGen5 template fnord null =>
+THIS TEXT SHOULD BE DELETED!!
+<= ENDIF =>
+_EOF_
+
+ag_err 5 -T ${testname}.tpl5 ${agopts}5 > /dev/null
+test -f ${testname}-task5-OK \
+ && failure unexpected success for test-5
+
+${EGREP} -v 'Giving up in .*functions\.c' ${testname}.err5 > ${testname}.res5
+cat > ${testname}.base5 <<'EOF'
+Error in template error.tpl5, line 3
+ Unknown macro or invalid context in error.tpl5 line 3:
+ ENDIF
+EOF
+
+cmp -s ${testname}.base5 ${testname}.res5 || \
+ failure "expected->actual${nl}`diff -c ${testname}.base5 ${testname}.res5`"
+
+# # # # # # # # # # # # # TEST 6 # # # # # # # # # # # #
+
+echo creating ${testname}.tpl6
+cat > ${testname}.tpl6 <<'_EOF_'
+<= AutoGen5 template fnord null =>
+This is a broken template, "nothing" is not defined.
+<= (
+if (exist? "nothing")
+ (error "it is broken
+ 'cuz it's okay")
+ (stumble-over-unbound-variable)
+) =>
+End Of File
+_EOF_
+
+ag_err 6 -T ${testname}.tpl6 ${agopts}6 > /dev/null
+test -f ${testname}-task6-OK \
+ && failure unexpected success for test-6
+
+cat > ${testname}.base6 <<EOF
+Scheme evaluation error. AutoGen ABEND-ing in template
+ error.tpl6 on line 3
+Failing Guile command: = = = = =
+
+(
+if (exist? "nothing")
+ (error "it is broken
+ 'cuz it's okay")
+ (stumble-over-unbound-variable)
+)
+
+=================================
+EOF
+
+# Guile keeps changing its mind about what it ought to print in the
+# face of an unbound variable. So, the heck with it. We've tried to
+# print file and line, but it was made just too hard. Strip out all
+# the Guile library error messages. :(
+#
+${SED} -n '/^Scheme evaluation error./,$p' ${testname}.err6 | \
+ ${SED} "/^${testname}\.tpl6:7:4:/d" > ${testname}.res6
+
+cmp -s ${testname}.base6 ${testname}.res6 || \
+ failure "expected->actual${nl}`diff -c ${testname}.base6 ${testname}.res6`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of error.test
diff --git a/agen5/test/expr.test b/agen5/test/expr.test
new file mode 100755
index 0000000..3e6eeda
--- /dev/null
+++ b/agen5/test/expr.test
@@ -0,0 +1,107 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# expr.test --- [= % name (sprintf "%%-18s" "%s") =] expression
+#
+# Time-stamp: "2012-04-01 06:07:01 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # TEMPLATE & DEFINITIONS FILES # # # # # # # # #
+#
+# We construct this by pulling down the sources from the
+# AutoGen source directory. We will also copy out the same
+# commands as are used there. ${top_srcdir} is a full path.
+#
+cp ${top_srcdir}/agen5/snarf.tpl ${top_srcdir}/agen5/exp*.c \
+ ${top_srcdir}/agen5/func*.c ${top_srcdir}/agen5/agShell.c ${TMPDIR}/.
+f=`fgrep '#ifdef DEBUG_ENABLED' ${top_srcdir}/agen5/expr.ini`
+test -n "$f" && {
+ DEBUG_ENABLED=true
+ export DEBUG_ENABLED
+}
+
+exec 3> ${TMPDIR}/${testname}.sh
+cat >&3 <<- _EOF_
+ GDexe=${GDexe}
+ CLexe=${CLexe}
+ srcdir=`pwd`
+ export GDexe CLexe srcdir
+ cd ${TMPDIR}
+ set -e
+ _EOF_
+
+if ${VERBOSE}
+then
+ cat >&3 <<- _EOF_
+ PS4='>${testname}-\${FUNCNAME-mkexp}> '
+ set -x
+ run_ag()
+ {
+ tfile=ag-\${1}-$$.log
+ tag="-MF${testname}-stamp-\${1} --trace=every --trace-out=\${tfile}"
+ shift
+ ${AGexe} -L${top_srcdir}/autoopts/tpl \${tag} "\$@"
+ }
+ _EOF_
+else
+ cat >&3 <<- _EOF_
+ run_ag()
+ {
+ tag="-MF${testname}-stamp-\${1}"
+ shift
+ echo ${AGexe} "\$@"
+ ${AGexe} -L${top_srcdir}/autoopts/tpl \${tag} "\$@"
+ }
+ _EOF_
+fi
+
+${SED} -n '/^make_exprini()/,/^[}]$/p' ${top_srcdir}/agen5/mk-stamps.sh | \
+ ${SED} 's/rm -f /echo rm -f/' >&3
+cat >&3 <<- _EOF_
+ make_exprini
+ ${SED} "${sed_omit_license}" expr.ini > \${srcdir}/expr.res
+ ${SED} "${sed_omit_license}" ${top_srcdir}/agen5/expr.ini \
+ > \${srcdir}/expr.base
+ _EOF_
+exec 3>&-
+
+# Remove the lines that *will* be different, always...
+#
+${SHELLX} ${TMPDIR}/${testname}.sh || failure "${testname}.sh failed"
+
+test -s expr.res || \
+ failure "${testname} test produced no output"
+
+cmp expr.base expr.res || \
+ failure "`diff -c expr.base expr.res`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of expr.test
diff --git a/agen5/test/extract.test b/agen5/test/extract.test
new file mode 100755
index 0000000..1e3e4c0
--- /dev/null
+++ b/agen5/test/extract.test
@@ -0,0 +1,80 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# extract.test --- test extract functionality
+#
+# Time-stamp: "2010-06-26 16:05:58 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+mkdir ${testname}.dir
+echo creating ${testname}.dir/${testname}.tpl
+cat > ${testname}.dir/${testname}.tpl <<_EOF_
+[= AutoGen5 template out =]
+This is the start of generated text[=\` echo ' working' \` =]
+We will be extracting from [=(find-file "${testname}" "sample")=]:
+[=(extract (find-file "${testname}" "sample")
+ "/* =-= %s =-= MARKER =-= %s =-= */")=]
+[=(extract (find-file "${testname}" "sample")
+ "/* =-= %s =-= SECOND =-= %s =-= */"
+ "DNC Test" "DEFAULT-ed text.")=]
+And this is the end.[= \` echo ' done' \` =]
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo creating ${testname}.dir/${testname}.sample
+# this is the output we should expect to see
+cat > ${testname}.dir/${testname}.sample <<_EOF_
+This is the start of generated text working
+We will be extracting from ${testname}.dir/${testname}.sample:
+_EOF_
+cat >> ${testname}.dir/${testname}.sample <<\_EOF_
+/* =-= START =-= MARKER =-= DO NOT CHANGE THIS COMMENT =-= */
+This is inserted text
+/* =-= END =-= MARKER =-= DO NOT CHANGE THIS COMMENT =-= */
+/* =-= START =-= SECOND =-= DNC Test =-= */
+This is an insertion
+test.
+/* =-= END =-= SECOND =-= DNC Test =-= */
+And this is the end. done
+_EOF_
+
+AGCMD="--no-def -L${testname}.dir -b${testname} -T${testname}.tpl"
+
+run_ag x ${AGCMD} || failure autogen ${AGCMD} failed
+
+cmp -s ${testname}.out ${testname}.dir/${testname}.sample || \
+ failure "`diff -c ${testname}.out ${testname}.dir/${testname}.sample`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of extract.test
diff --git a/agen5/test/for.test b/agen5/test/for.test
new file mode 100755
index 0000000..45a2f55
--- /dev/null
+++ b/agen5/test/for.test
@@ -0,0 +1,112 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# for.test --- test functionality of `for' function
+#
+# Time-stamp: "2011-02-02 12:09:29 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+${SED} 's/^ *[0-9]*: //' > $testname.tpl <<_EOF_
+ 1: <= AutoGen5 template test =>
+ 2: <=
+ 3: FOR list ",\n" =><=
+ 4: IF (first-for?)
+ 5: =>FIRST: <=
+ 6: ELIF (last-for?)
+ 7: =>LAST: <=
+ 8: ELSE
+ 9: =>MIDDLE: <=
+10: ENDIF =><=elt[0]=><=
+11: ENDFOR =>
+12: <=
+13: FOR list-2 \=>
+14: <=FOR list ", "\=>
+15: <=bumble \=>
+16: <=ENDFOR \=>
+17: <=
+18: ENDFOR =>
+19: <=
+20: FOR list ",\n" =><=
+21: FOR list-2 \=>
+22: <=(for-index "list")=>: <=
+23:
+24: IF (first-for? "list")
+25: =>FIRST <=
+26: ELIF (last-for? "list")
+27: =>LAST <=
+28: ELSE
+29: =>MIDDLE <=
+30: ENDIF =><=
+31:
+32: (tpl-file-line " on line %2\$d") =><=
+33:
+34: ENDFOR =><=
+35: ENDFOR =>
+_EOF_
+
+
+# Create the files we need in the test environment
+cat > $testname.def <<_EOF_
+AutoGen definitions $testname;
+list = { elt = one; },
+{ elt = two.one, two.two, two.three; };
+list = { elt = three; }, {elt = four;};
+
+list-2 = { bumble = mumble; };
+_EOF_
+
+# this is the output we should expect to see
+cat > $testname.sample <<_EOF_
+FIRST: one,
+MIDDLE: two.one,
+MIDDLE: three,
+LAST: four
+mumble, mumble, mumble, mumble
+0: FIRST on line 32,
+1: MIDDLE on line 32,
+2: MIDDLE on line 32,
+3: LAST on line 32
+_EOF_
+
+run_ag x $testname.def || failure AutoGen failed
+cmp -s $testname.test $testname.sample || \
+ failure "`diff -c $testname.test $testname.sample`"
+
+echo '<= FOR =><= ENDFOR =>' >> $testname.tpl
+f=`(run_ag x $testname.def 2>&1) | ${EGREP} 'requires iterator'`
+test -z "$f" && \
+ failure "failed to detect missing FOR iterator"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of for.test
diff --git a/agen5/test/forfrom.test b/agen5/test/forfrom.test
new file mode 100755
index 0000000..ea4a3a2
--- /dev/null
+++ b/agen5/test/forfrom.test
@@ -0,0 +1,79 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+#
+# forfrom.test --- test forfrom functionality
+#
+# Time-stamp: "2010-07-17 09:52:04 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+#
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+cat > $testname.tpl <<_EOF_
+<= AutoGen5 template test =>
+<=
+
+FOR list (for-by 3) (for-sep ",\n")
+
+ =>list[<=(for-index)=>] = <=
+ ?% elt 'this is "%s"' EMPTY=><=
+
+ENDFOR
+
+=>
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+cat > $testname.def <<EOF
+AutoGen definitions $testname;
+list[1] = { elt = one; };
+list[3] = { elt = three; };
+list[5] = { elt = five; };
+list[7] = { elt = seven; };
+list[9] = { elt = nine; };
+EOF
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo creating $testname.out
+# this is the output we should expect to see
+cat > $testname.out <<'_EOF_'
+list[1] = this is "one",
+list[4] = EMPTY,
+list[7] = this is "seven"
+_EOF_
+
+run_ag x $testname.def || failure autogen failed
+cmp -s $testname.test $testname.out || failure unexpected output
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of forfrom.test
diff --git a/agen5/test/forin.test b/agen5/test/forin.test
new file mode 100755
index 0000000..313d887
--- /dev/null
+++ b/agen5/test/forin.test
@@ -0,0 +1,86 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+#
+# forin.test --- test forin functionality
+#
+# Time-stamp: "2010-07-17 09:51:55 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+#
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+set -x
+echo creating ${testname}.tpl in `pwd`
+cat > ${testname}.tpl <<_EOF_
+[+ AutoGen5 template out +]
+[+
+FOR t IN build host target +][+
+ FOR v IN alias cpu vendor os +]
+[+t+]_[+v+]=@[+t+]_[+v+]@[+
+ ENDFOR +]
+[+t+]_canonical=@[+t+]_cpu@-@[+t+]_vendor@-@[+t+]_os@
+[+
+
+ENDFOR
+
++]
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo creating ${testname}.ok
+# this is the output we should expect to see
+cat > ${testname}.ok <<'_EOF_'
+
+build_alias=@build_alias@
+build_cpu=@build_cpu@
+build_vendor=@build_vendor@
+build_os=@build_os@
+build_canonical=@build_cpu@-@build_vendor@-@build_os@
+
+host_alias=@host_alias@
+host_cpu=@host_cpu@
+host_vendor=@host_vendor@
+host_os=@host_os@
+host_canonical=@host_cpu@-@host_vendor@-@host_os@
+
+target_alias=@target_alias@
+target_cpu=@target_cpu@
+target_vendor=@target_vendor@
+target_os=@target_os@
+target_canonical=@target_cpu@-@target_vendor@-@target_os@
+
+_EOF_
+
+run_ag x --no-def -T${testname}.tpl -b ${testname} || \
+ failure autogen failed
+cmp -s ${testname}.ok ${testname}.out || \
+ failure "`diff ${testname}.ok ${testname}.out`"
+exit 0
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of forin.test
diff --git a/agen5/test/format.test b/agen5/test/format.test
new file mode 100755
index 0000000..87d88c9
--- /dev/null
+++ b/agen5/test/format.test
@@ -0,0 +1,96 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# format.test --- Count format string arguments.
+#
+# Time-stamp: "2010-06-26 16:05:41 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[++ AutoGen5 template test ++]
+[++ (dne "-d" "# ") ++]
+[++
+
+FOR fmt "\n"
+
+++]Fmt [++(+ 1 (for-index))++] has [++(format-arg-count (get "fmt"))
+++] args: [++(c-string (get "fmt"))++][++
+
+ENDFOR
+
+++]
+[++(hide-email "Bruce Korb" "bkorb@gnu.org")++]
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating ${testname}.def
+cat > ${testname}.def <<_EOF_
+AutoGen definitions ${testname};
+fmt = "none";
+fmt = "one %s val";
+fmt = "two %s (%d%%) vals";
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.base
+# this is the output we should expect to see
+cat > ${testname}.base <<'_EOF_'
+# DO NOT EDIT THIS FILE (format.test)
+#
+# It has been AutoGen-ed
+# From the definitions format.def
+# and the template file format
+Fmt 1 has 0 args: "none"
+Fmt 2 has 1 args: "one %s val"
+Fmt 3 has 2 args: "two %s (%d%%) vals"
+<script language="JavaScript" type="text/javascript">
+<!--
+var one = 'm&#97;';
+var two = 'i&#108;t';
+document.write('<a href="' + one + two );
+document.write('&#111;:&#98;&#107;&#111;&#114;&#98;&#64;&#103;&#110;&#117;&#46;&#111;&#114;&#103;');
+document.write('" >Bruce Korb</a>');
+//-->
+</script>
+_EOF_
+
+run_ag x ${testname}.def || failure autogen failed
+cmp -s ${testname}.base ${testname}.test || \
+ failure "`diff ${testname}.base ${testname}.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of format.test
diff --git a/agen5/test/get.test b/agen5/test/get.test
new file mode 100755
index 0000000..7800a9a
--- /dev/null
+++ b/agen5/test/get.test
@@ -0,0 +1,95 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# get.test --- test get functionality
+#
+# Time-stamp: "2011-03-06 15:26:30 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+cat > $testname.tpl <<_EOF_
+[= AutoGen5 template test =]
+[=
+
+IF (not (exist? "foo.bar.baz")) =][=
+ (error "foo.bar.baz does, too, exist!") =][=
+
+ELIF (not (= "mumble" (get "foo.bar.baz"))) =][=
+ (error (sprintf "We got the wrong baz. We got: %s"
+ (get "foo.bar.baz"))) =][=
+ELSE
+ =]A OK[=
+ENDIF =][=
+
+- bogus "\nA OK2" =][=
+- foo "\nBOGUS" =]
+[= (get "fuz-zy") =]
+[= (get "fuz_zy") =]
+[= (get "fuz^zy") =]
+[= (get-c-name "fuz-zy") =]
+[= (get-up-name "fuz-zy") =]
+[= (get-down-name "fuz-zy") =]
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+cat > $testname.def <<_EOF_
+AutoGen definitions $testname;
+
+foo = { bar = { nada = nothing; }; };
+foo = { bar = { baz = mumble; }; }; /* this is the one */
+foo = { bar = { baz = grumble; }; };
+fuz-zy = Mum-Ble;
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating $testname.out
+# this is the output we should expect to see
+cat > $testname.out <<'_EOF_'
+A OK
+A OK2
+Mum-Ble
+Mum-Ble
+Mum-Ble
+Mum_Ble
+MUM_BLE
+mum_ble
+_EOF_
+
+run_ag x $testname.def || failure autogen failed
+cmp -s $testname.test $testname.out || failure unexpected output
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of get.test
diff --git a/agen5/test/gperf.test b/agen5/test/gperf.test
new file mode 100755
index 0000000..aa4746f
--- /dev/null
+++ b/agen5/test/gperf.test
@@ -0,0 +1,95 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# gperf.test --- test functionality of `gperf'
+#
+# Time-stamp: "2010-12-06 13:08:36 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+# ----------------------------------------------------------------------
+
+if gperf --version > /dev/null 2>&1
+then :
+else
+ echo gperf functionality does not work without gperf >&2
+ exit 0
+fi
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+<= AutoGen5 template test =>
+<=
+
+(if (not (make-gperf "${testname}" (stack "foo.bar")))
+ (error "cannot make gperf")) =><=
+
+FOR foo "\n" =><=
+
+ FOR bar =>
+<=bar=> yields: <=(gperf "${testname}" (get "bar"))=><=
+ ENDFOR =><=
+
+ENDFOR =><=
+\`tar cf - .${testname}.* > ${testname}-prog.tar || \
+ die 'cannot save gperf files'\`=><=
+(out-push-new "${testname}-code.c")
+(emit (gperf-code "${testname}")) (emit "\n")
+(out-pop)
+=>
+_EOF_
+
+
+# Create the files we need in the test environment
+cat > ${testname}.def <<_EOF_
+AutoGen Definitions ${testname};
+foo = { bar = first; bar = second; };
+foo = { bar = third; bar = fourth; };
+_EOF_
+
+# this is the output we should expect to see
+cat > ${testname}.base <<_EOF_
+
+first yields: 0x01
+second yields: 0x02
+
+third yields: 0x03
+fourth yields: 0x04
+_EOF_
+
+if ${VERBOSE:-false}
+then opts="x --trace=server-shell ${testname}.def"
+else opts="x ${testname}.def"
+fi
+
+run_ag ${opts} || failure AutoGen failed
+cmp -s ${testname}.base ${testname}.test || \
+ failure "`diff ${testname}.base ${testname}.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of gperf.test
diff --git a/agen5/test/heredef.test b/agen5/test/heredef.test
new file mode 100755
index 0000000..959fb92
--- /dev/null
+++ b/agen5/test/heredef.test
@@ -0,0 +1,102 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# heredef.test --- definition reference testing
+#
+# Time-stamp: "2011-02-02 12:09:38 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+${SED} 's/^ *[0-9]*: //' > ${testname}.tpl <<\_EOF_
+ 1: <= AutoGen5 template test =>
+ 2: <=
+ 3:
+ 4: (if (not (= (get "string1") (get "string2")))
+ 5: (error (sprintf "`%s' <> `%s'"
+ 6: (get "string1") (get "string2") )) )
+ 7:
+ 8: =><=
+ 9:
+10: (if (not (= (get "string2") (get "string3")))
+11: (error (sprintf "`%s' <> `%s'"
+12: (get "string1") (get "string2") )) )
+13: =>OKAY: <= (c-string (get "string3")) =>
+14: FROM: string1 extracted <= (def-file-line "string1") =>
+15: string2 extracted
+16: <= (def-file-line "string2" c-file-line-fmt) =>
+17: string3 extracted <=
+18: (def-file-line "string3" "From %1$s on line %2$d") =>
+_EOF_
+
+# # # # # # # # # # SAMPLE FILE # # # # # # # # #
+cat > ${testname}.sample <<\_EOF_
+OKAY: "\"`Testing'"
+FROM: string1 extracted from heredef.def line 11
+ string2 extracted
+#line 15 "heredef.def"
+
+ string3 extracted From heredef.def on line 18
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+# Create the files we need in the test environment
+echo "AutoGen definitions ${testname};" > ${testname}.def
+${SED} 's/^ *[0-9]*: //' >> ${testname}.def <<\_EOF_
+ 2: #ifdef BOGUS
+ 3: /* Line 3 */
+ 4: #else
+ 5: #ifdef WRONG
+ 6: /* line 6 */
+ 7: #endif
+ 8: #endif
+ 9: /* line 9 */
+10: string1 = <<- AG_EOF
+11: "`Testing'
+12: AG_EOF; /* " */
+13: /* line 13 */
+14: string2 = <<- AG_EOF
+15: "`Testing'
+16: AG_EOF; /* " */
+17:
+18: string3 = "\"`Testing'"; /* line 18 */
+19:
+_EOF_
+
+run_ag x ${testname}.def || failure ${testname} AutoGen failed
+
+if cmp ${testname}.test ${testname}.sample
+then cleanup
+else failure "`diff ${testname}.sample ${testname}.test`"
+fi
+
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of heredef.test
diff --git a/agen5/test/html.test b/agen5/test/html.test
new file mode 100755
index 0000000..8ed4fb8
--- /dev/null
+++ b/agen5/test/html.test
@@ -0,0 +1,130 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# html.test --- test html generation
+#
+# Time-stamp: "2011-12-30 12:17:28 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILES # # # # # # # # #
+
+echo creating cgi.tpl
+cat > cgi.tpl <<'_EOF_'
+<? AutoGen5 Template ?>
+<? DEFINE form-error
+?>HTTP/1.0 500 AutoGen Forms Error
+Content-Type: text/plain
+
+The submitted form does not contain a valid template<?
+ IF (exist? "template") ?> (<?template?>) <?ENDIF?>.<?
+ENDDEF ?><?
+ (define foo "")
+ (if (exist? "break") (set! foo bogus)) ?><?
+IF (not (exist? "template")) ?><?
+ form-error ?><?
+
+ELIF (access? (get "template") R_OK) ?><?
+ INCLUDE (get "template") ?><?
+
+ELIF (access? (string-append
+ (get "template") ".tpl") R_OK) ?><?
+ INCLUDE (string-append (get "template") ".tpl") ?><?
+
+ELSE ?><?
+ form-error ?><?
+ENDIF ?>
+_EOF_
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<'_EOF_'
+<? AutoGen5 Template ?>
+<head>Mumble: <? mumble ?></head>
+<body>Foolish: <? foo ?></body>
+_EOF_
+
+# # # # # # # SAMPLE OUTPUT FILE # # # # # #
+
+echo creating ${testname}.out in `pwd`
+# this is the output we should expect to see
+cat > ${testname}.samp <<'_EOF_'
+content-type: text/html
+
+<head>Mumble: fumble bumble</head>
+<body>Foolish: bar</body>
+_EOF_
+
+# # # # # # # RUN AUTOGEN # # # # # #
+
+REQUEST_METHOD=GET
+QUERY_STRING="mumble=fumble+bumble&foo=bar&template=${testname}"
+CONTENT_LENGTH=`expr "${QUERY_STRING}" : ".*"`
+
+export CONTENT_LENGTH REQUEST_METHOD QUERY_STRING
+
+run_ag x1 | ${EGREP} -v '^in state' > ${testname}.test
+
+cmp -s ${testname}.samp ${testname}.test || \
+ failure "`diff ${testname}.samp ${testname}.test`"
+
+# # # # # # # SECOND RUN # # # # # #
+
+QUERY_STRING="${QUERY_STRING}&break=true"
+CONTENT_LENGTH=`expr "${QUERY_STRING}" : ".*"`
+
+run_ag x2 > ${testname}-2.test
+fgrep 'AutoGen form processing error' ${testname}-2.test || \
+ failure autogen unexpectedly succeeded
+
+cat > ${testname}-2.samp <<'_EOF_'
+Content-type: text/plain
+
+AutoGen form processing error:
+cgi.tpl:9:22: In expression (define foo bogus):
+cgi.tpl:9:22: Unbound variable: bogus
+Scheme evaluation error. AutoGen ABEND-ing in template
+ cgi.tpl on line 9
+Failing Guile command: = = = = =
+
+(define foo "")
+ (if (exist? "break") (set! foo bogus))
+
+=================================
+_EOF_
+${EGREP} -v '^in state' ${testname}-2.test > ${testname}-2.res
+cmp -s ${testname}-2.samp ${testname}-2.res || {
+ exec >&2
+ echo Your Guile library does not handle error traps correctly and causes
+ echo garbage to be emitted instead. Do not use this autogen as a CGI service.
+}
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of html.test
diff --git a/agen5/test/in.test b/agen5/test/in.test
new file mode 100755
index 0000000..c3793a9
--- /dev/null
+++ b/agen5/test/in.test
@@ -0,0 +1,95 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# in.test --- Verify that the "in?" predicate works
+#
+# Time-stamp: "2010-06-26 16:05:12 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[++ AutoGen5 template out ++][++
+
+(define stack-list (stack "foo.bar"))
+(string-append
+ (if (in? (get "baz") stack-list) "success" "FAIL")
+ "\n"
+ (if (in? (get "oops") stack-list) "FAIL" "success")
+) ++][++
+
+IF .true ++]
+success[++
+ELSE ++]
+FAILURE[++
+ENDIF ++]-[++ .true ++][++
+
+IF .false ++]
+FAILURE[++
+ELSE ++]
+success[++
+ENDIF ++]-[++ .false ++]
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating ${testname}.def
+cat > ${testname}.def <<_EOF_
+AutoGen definitions ${testname};
+foo = { bar = one; };
+foo = { bar = two; };
+foo = { bar = three; };
+foo = { bar = four; };
+baz = three;
+oops = oops;
+true = true;
+false = false;
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.ok
+# this is the output we should expect to see
+cat > ${testname}.ok <<'_EOF_'
+success
+success
+success-true
+success-false
+_EOF_
+
+run_ag x ${testname}.def || failure autogen failed
+cmp -s ${testname}.ok ${testname}.out || \
+ failure "`diff ${testname}.ok ${testname}.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of in.test
diff --git a/agen5/test/include.test b/agen5/test/include.test
new file mode 100755
index 0000000..2b1ca88
--- /dev/null
+++ b/agen5/test/include.test
@@ -0,0 +1,80 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# include.test --- test include functionality
+#
+# Time-stamp: "2012-08-11 08:03:16 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.in
+cat > ${testname}.in <<_EOF_
+<= AutoGen5 Template
+# this is just a test
+test =>
+<= v-name =>: <= \`echo This is an ${testname} test\` =>
+_EOF_
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[= AutoGen5 Template
+# this is just a test
+
+test =]
+[= Include "${testname}.in" =]
+_EOF_
+
+mkdir ${testname}.d ${testname}.d/good
+cat > ${testname}.d/${testname}.def <<- _EOF_
+ AutoGen Definitions ${testname}.tpl;
+ #include good/${testname}-aux.def
+ _EOF_
+
+echo "v-name = Verify;" > ${testname}.d/good/${testname}-aux.def
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo creating ${testname}.ok
+# this is the output we should expect to see
+cat > ${testname}.ok <<_EOF_
+Verify: This is an ${testname} test
+_EOF_
+
+AGCMD="-L ${testname}.d/bad -L ${testname}.d/bad/good"
+
+run_ag x ${AGCMD} ${testname}.d/${testname}.def || \
+ failure ${AGCMD} failed
+cmp -s ${testname}.test ${testname}.ok || \
+ failure "unexpected output
+ `diff -u ${testname}.test ${testname}.ok `"
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of include.test
diff --git a/agen5/test/leave.test b/agen5/test/leave.test
new file mode 100755
index 0000000..9476d49
--- /dev/null
+++ b/agen5/test/leave.test
@@ -0,0 +1,105 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# leave.test --- test return/next/break functionality
+#
+# Time-stamp: "2012-04-07 09:03:06 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILES # # # # # # # # #
+
+echo creating $testname.tlib
+cat > $testname.tlib <<'_EOF_'
+[= AutoGen5 Template test =]
+[= RETURN =]
+BOGUS
+_EOF_
+
+echo creating $testname.tpl
+cat > $testname.tpl <<_EOF_
+[= AutoGen5 Template test =]
+[=
+INVOKE macro
+=][=
+DEFINE macro =][=
+
+FOR value =][=
+ IF (= 1 (for-index)) =][= BREAK =][= ENDIF =]
+BOGUS
+[= ENDFOR =][=
+
+FOR value =][=
+ CASE (for-index) =][=
+ == 4 =][=
+ * =][= CONTINUE =][=
+ ESAC =][= (for-index) =] Okay.[=
+ INCLUDE "$testname.tlib" =][=
+
+ RETURN \=]
+
+BOGUS
+[= ENDFOR =][= ENDDEF =]
+_EOF_
+
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+cat > $testname.def <<_EOF_
+autogen definitions $testname;
+
+value[1] = first;
+value[2] = secondary;
+value[4] = tertiary;
+value[6] = last;
+
+_EOF_
+
+# this is the output we should expect to see
+echo 4 Okay. > $testname.samp
+
+run_ag x $testname.def || \
+ failure "autogen failed"
+set -x
+cmp -s $testname.samp $testname.test || \
+ failure "`diff $testname.samp $testname.test`"
+
+# # # # # # # # # # TEMPLATE FILES # # # # # # # # #
+
+echo creating $testname.tlib
+cat > $testname.tlib <<'_EOF_'
+[= AutoGen5 Template test =]
+[= BREAK =]
+BOGUS
+_EOF_
+
+run_ag x -b $testname-bad $testname.def 2>/dev/null && \
+ failure "processed broken template"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of leave.test
diff --git a/agen5/test/license.test b/agen5/test/license.test
new file mode 100755
index 0000000..5925acf
--- /dev/null
+++ b/agen5/test/license.test
@@ -0,0 +1,114 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# license.test --- test license functionality
+#
+# Time-stamp: "2010-06-26 16:05:08 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[= AutoGen5 template test =]
+/*
+[= (license "${testname}" "${testname}" "Auto-Gen" " * " ) =]
+ */
+_EOF_
+
+# # # # # # # # # # LICENSE FILE # # # # # # # # #
+
+echo creating ${testname}.lic
+cat > ${testname}.lic <<'_EOF_'
+This is a bogus license granted by %2$s for %1$s.
+Use it in good health
+_EOF_
+
+# # # # # # # # # # EXTEND FILES TO PAGESIZE # # # # # # # # #
+
+cat > ${testname}-extend.c <<- _EOF_
+ #define HAVE_CONFIG_H 1
+ #include "config.h"
+ #include "compat/compat.h"
+
+ int main( int argc, char** argv ) {
+ char z_tail[] = "=]\n */\n";
+ long offset = 0L - (sizeof(z_tail) - 1);
+ struct stat sb;
+ char* file;
+ size_t sz;
+ FILE* fp;
+
+ file = *++argv;
+ fp = fopen(file, "a");
+ if (fp == NULL) return 1;
+ if (stat(file, &sb) != 0) return 1;
+ sz = 0x2000 - (sb.st_size & 0x1FFFUL);
+ while (sz > 0) { putc( '\n', fp ); sz--; }
+ fclose(fp);
+
+ file = *++argv;
+ fp = fopen( file, "r+" );
+ if (fp == NULL) return 1;
+ if (stat(file, &sb) != 0) return 1;
+ fseek(fp, offset, SEEK_END);
+ sz = 0x2000 - (sb.st_size & 0x1FFFUL);
+ while (sz > 0) { putc( '\n', fp ); sz--; }
+ fputs(z_tail, fp);
+ fclose(fp);
+
+ return 0; }
+ _EOF_
+
+Csrc=${testname}-extend
+compile
+
+./${testname}-extend ${testname}.lic ${testname}.tpl || \
+ failure "Could not extend license/template files to 8KB"
+ls -l ${testname}.???
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.out
+# this is the output we should expect to see
+cat > ${testname}.out <<_EOF_
+/*
+ * This is a bogus license granted by Auto-Gen for ${testname}.
+ * Use it in good health
+ */
+_EOF_
+
+run_ag x -b ${testname} --no-def -T ${testname}.tpl || \
+ failure autogen failed
+cmp -s ${testname}.test ${testname}.out || failure unexpected output
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of license.test
diff --git a/agen5/test/line.test b/agen5/test/line.test
new file mode 100755
index 0000000..c0462f0
--- /dev/null
+++ b/agen5/test/line.test
@@ -0,0 +1,68 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# line.test --- test (tpl-file-line) functionality
+#
+# Time-stamp: "2011-02-02 12:09:53 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+${SED} 's/^ *[0-9]*: //' > ${testname}.tpl << _EOTPL_
+ 1: [= AutoGen5 Template txt =]
+ 2: ${testname} Test
+ 3: [= (define ix 0) (define ct 63) =]
+ 4: [=WHILE (< ix ct) \=]
+ 5: [= (set! ix (+ ix 1))
+ 6: (sprintf "test %2d of %2d: %s\n" ix ct (tpl-file-line "%2\$d"))
+ 7: \=]
+ 8: [=ENDWHILE \=]
+ 9: End ${testname} Test
+_EOTPL_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.out
+# this is the output we should expect to see
+cat > ${testname}.out <<- \_EOF_
+ line Test
+
+ End line Test
+ _EOF_
+
+run_ag x --base=${testname} --no-def --override=${testname}.tpl || \
+ failure autogen failed
+${EGREP} -v ' of 63: 5$' ${testname}.txt > ${testname}.test
+
+cmp -s ${testname}.test ${testname}.out || failure unexpected output
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of line.test
diff --git a/agen5/test/loop.test b/agen5/test/loop.test
new file mode 100755
index 0000000..ef92af5
--- /dev/null
+++ b/agen5/test/loop.test
@@ -0,0 +1,78 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# loop.test --- test FOR loop
+#
+# Time-stamp: "2010-06-26 16:04:59 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+limit=32
+cat > ${testname}.tpl <<- _EOTPL_
+ [= AutoGen5 Template test =]
+ ${testname} Test
+ [= (define ix 0) (define ct ${limit}) =][=
+
+ WHILE (< ix ct) =][=
+ (set! ix (+ ix 1)) =][=
+ (sprintf "test %2d of %2d: %s\n"
+ ix ct (tpl-file-line "%2\$d")) =][=
+ ENDWHILE
+
+ =]End ${testname} Test
+ _EOTPL_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.base
+
+# this is the output we should expect to see
+#
+exec 3> ${testname}.base
+echo "${testname} Test" >&3
+ix=1
+while test $ix -le $limit
+do printf "test %2d of ${limit}: 7\n" $ix
+ ix=`expr $ix + 1`
+done >&3
+echo "End ${testname} Test" >&3
+exec 3>&-
+
+run_ag x --base=${testname} --no-def --override=${testname}.tpl || \
+ failure autogen failed
+
+cmp -s ${testname}.base ${testname}.test || \
+ failure "unexpected output: `diff ${testname}.base ${testname}.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of loop.test
diff --git a/agen5/test/make.test b/agen5/test/make.test
new file mode 100755
index 0000000..cfa55dd
--- /dev/null
+++ b/agen5/test/make.test
@@ -0,0 +1,91 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# make.test --- test makefile script manufacture
+#
+# Time-stamp: "2010-06-26 16:04:55 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<'_EOF_'
+[= AutoGen5 Template
+
+=]
+== This source:
+[=
+
+ (out-push-new)
+
+=]
+foo=`pwd` && ls -l $foo
+bar=$foo continue=command \
+make macros: $(MAKE) $* $@ $< $% $?
+shell vars: ${MAKE} $# $F ${?} ${*} $$
+[=
+(define txt (out-pop #t))
+(string-append txt "\n== converts to:\n\n" (makefile-script txt))
+=]
+_EOF_
+
+# # # # # # # SAMPLE OUTPUT FILE # # # # # #
+
+echo creating ${testname}.out in `pwd`
+# this is the output we should expect to see
+cat > ${testname}.samp <<'_EOF_'
+== This source:
+
+foo=`pwd` && ls -l $foo
+bar=$foo continue=command \
+make macros: $(MAKE) $* $@ $< $% $?
+shell vars: ${MAKE} $# $F ${?} ${*} $$
+
+== converts to:
+
+ foo=`pwd` && ls -l $$foo ; \
+ bar=$$foo continue=command \
+ make macros: $(MAKE) $* $@ $< $% $? ; \
+ shell vars: $${MAKE} $$# $$F $${?} $${*} $$$$
+_EOF_
+
+# # # # # # # RUN AUTOGEN # # # # # #
+
+run_ag x -T ${testname}.tpl --no-def > ${testname}.test || \
+ failure autogen failed
+
+# # # # # # # TEST RESULTS # # # # # #
+
+cmp -s ${testname}.samp ${testname}.test || \
+ failure "`diff ${testname}.samp ${testname}.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of make.test
diff --git a/agen5/test/match.test b/agen5/test/match.test
new file mode 100755
index 0000000..def13a3
--- /dev/null
+++ b/agen5/test/match.test
@@ -0,0 +1,72 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# match.test --- test the "match-value?" scheme
+#
+# Time-stamp: "2010-06-26 16:04:51 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<'_EOF_'
+[= AutoGen5 Template test =]
+ ==* "sec" [= IF (match-value? ==* "foo.valu" "sec" )
+ =]YES[=ELSE=]FAIL[=ENDIF=]
+ ==* "SEC" [= IF (match-value? ==* "foo.valu" "SEC" )
+ =]FAIL[=ELSE=]YES[=ENDIF=]
+ *=* "rST" [= IF (match-value? *=* "foo.valu" "rST" )
+ =]YES[=ELSE=]FAIL[=ENDIF=]
+ *==* "rST" [= IF (match-value? *==* "foo.valu" "rST" )
+ =]FAIL[=ELSE=]YES[=ENDIF=]
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating ${testname}.def
+cat > ${testname}.def <<_EOF_
+autogen definitions ${testname}.tpl;
+
+foo = { valu = first.0; valu = primary.0; };
+foo = { valu = first.1; valu = secondary.1; };
+foo = { valu = first.2; };
+foo = { valu = first.3; valu = tertiery.3; };
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+run_ag x ${testname}.def || \
+ failure could not generate output
+${EGREP} FAIL ${testname}.test && \
+ failure some ${testname} tests failed
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of match.test
diff --git a/agen5/test/opts.test b/agen5/test/opts.test
new file mode 100755
index 0000000..489e30f
--- /dev/null
+++ b/agen5/test/opts.test
@@ -0,0 +1,91 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# opts.test --- Verify the handling of options
+#
+# Time-stamp: "2011-12-04 05:12:42 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. ./defs
+
+# Fetch the options files
+#
+unstamp()
+{
+ # The "zDetail" and zCopyrightNotice text gets formatted with "fmt -w 75".
+ # The different "fmt" implementations behave differently.
+ #
+ ${SED} -e "${sed_omit_license}" \
+ -e '/ extracted from.* line [1-9]/d' \
+ -e '/static char const zDetail/,/";$/d' \
+ -e '/static char const zCopyright/,/";$/d' \
+ -e '/static char const zLicenseDescrip/,/";$/d' \
+ -e '/ "autogen is free software: /,/www\.gnu\.org\/licenses/d' \
+ $1 > $2 ||
+ failure Cannot remove stamps from $1
+}
+
+workdir=`pwd`
+rm -f ../VERSION ./opts.* || :
+
+cd ${top_srcdir}/agen5
+unstamp opts.c ${workdir}/opts.c.base
+unstamp opts.h ${workdir}/opts.h.base
+cp opts.def ${workdir}/.
+cp -f ${top_srcdir}/VERSION ${workdir}/..
+
+cd ${workdir}
+set -x
+ls -l * ../VERSION > $testname.log
+
+echo Checking for "'define DEBUG'" options
+if ${GREP} 'define DEBUG' ${srcdir}/../opts.h
+then
+ AGCMD="-DDEBUG=1"
+else
+ AGCMD=""
+fi
+
+run_ag x ${AGCMD} opts.def || {
+ rm -f ../VERSION
+ failure ${AGCMD} opts.def
+}
+
+rm -f ../VERSION
+
+unstamp opts.c opts.c.res
+echo diff opts.c.base opts.c.res
+diff opts.c.base opts.c.res || \
+ failure "`diff -c opts.c.base opts.c.res`"
+
+unstamp opts.h opts.h.res
+echo diff opts.h.base opts.h.res
+diff opts.h.base opts.h.res || \
+ failure "`diff -c opts.h.base opts.h.res`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of opts.test
diff --git a/agen5/test/output.test b/agen5/test/output.test
new file mode 100755
index 0000000..06aeb72
--- /dev/null
+++ b/agen5/test/output.test
@@ -0,0 +1,120 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# include.test --- test include functionality
+#
+# Time-stamp: "2010-06-26 16:04:42 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+cat > $testname.tpl <<'EOF'
+<= AutoGen5 template test =>
+<=(out-push-new)
+
+=>This is sample output
+<= `echo echoed text.` =><=
+(out-suspend "here")
+(out-push-new)
+=>This text is from another diversion.
+<=
+(out-suspend "there")
+=>This is the first output text.
+<=(out-resume "here")
+=>
+Final text<=
+
+(define text (out-pop #t)) =><=
+
+DEFINE wrapper
+
+=>BEGIN
+<=(out-resume "there") (out-pop #t)
+=><=
+(. text)=>
+END<=
+
+ENDDEF wrapper =><=
+
+wrapper =>
+Done.
+EOF
+
+# # # # # # # SAMPLE OUTPUT FILE # # # # # #
+
+echo creating $testname.out in `pwd`
+# this is the output we should expect to see
+cat > $testname.samp <<'EOF'
+This is the first output text.
+BEGIN
+This text is from another diversion.
+This is sample output
+echoed text.
+Final text
+END
+Done.
+EOF
+
+# # # # # # # RUN AUTOGEN # # # # # #
+
+run_ag nodef -b $testname -T $testname.tpl --no-def || \
+ failure autogen failed
+
+# # # # # # # TEST RESULTS # # # # # #
+
+cmp -s $testname.samp $testname.test || \
+ failure "`diff $testname.samp $testname.test`"
+
+# # # # # # # EMPTY DEFINITIONS FILE # # # # # #
+
+cat > $testname.def <<EOF
+AutoGen Definitions $testname.tpl;
+EOF
+
+# # # # # # # RUN AUTOGEN # # # # # #
+
+mv -f $testname.test $testname.tst1 || \
+ failure moving output-1 failed
+
+run_ag def $testname.def || \
+ failure autogen failed
+
+mv -f $testname.test $testname.tst2 || \
+ failure moving output-2 failed
+
+# # # # # # # TEST RESULTS # # # # # #
+
+cmp -s $testname.tst1 $testname.tst2 || \
+ failure "`diff $testname.tst1 $testname.tst2`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of output.test
diff --git a/agen5/test/pseudo.test b/agen5/test/pseudo.test
new file mode 100755
index 0000000..f5b87c7
--- /dev/null
+++ b/agen5/test/pseudo.test
@@ -0,0 +1,103 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# pseudo.test --- test the select pseudo option
+#
+# Time-stamp: "2010-06-26 16:04:38 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<- \_EOF_
+ [= AutoGen5 Template
+
+ (define f-name "")
+
+ h=(begin
+ (set! f-name (getenv "incdir"))
+ (if (not (string? f-name)) (set! f-name "."))
+ (set! f-name (string-append f-name "/%s-hdr.%s"))
+ (shellf
+ "d=`dirname %s`
+ test -d ${d} || mkdir -p $d || die cannot mkdir $d"
+ f-name)
+ f-name
+ )
+
+ c=(begin
+ (set! f-name (getenv "srcdir"))
+ (if (not (string? f-name)) (set! f-name "."))
+ (set! f-name (string-append f-name "/%s-body.%s"))
+ (shellf
+ "d=`dirname %s`
+ test -d ${d} || mkdir -p $d || die cannot mkdir $d"
+ f-name)
+ f-name
+ )
+
+ # end of pseudo
+ =]
+ [=
+
+ (sprintf "two file create %s: " (suffix)) =][=
+
+ CASE (suffix) =][=
+
+ == h =]HEADER FILE[=
+ == c =]BODY 4 FILE[=
+ * =][= (error "woops") =][=
+
+ ESAC =]
+ _EOF_
+
+# # # # # # # # # # RUN TESTS # # # # # # #
+
+incdir=${testname}-inc
+srcdir=${testname}-src
+
+export incdir srcdir
+
+run_ag x --no-def -T${testname}.tpl -b${testname} || \
+ failure autogen --no-def -T${testname}.tpl -b${testname}
+
+f=${incdir}/${testname}-hdr.h
+test -f ${f} || \
+ failure missing file: ${f}
+${FGREP} 'two file create h: HEADER FILE' ${f} || failure bad contents for $f
+
+f=${srcdir}/${testname}-body.c
+test -f ${f} || \
+ failure missing file: ${f}
+${FGREP} 'two file create c: BODY 4 FILE' ${f} || failure bad contents for $f
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of pseudo.test
diff --git a/agen5/test/reorder.test b/agen5/test/reorder.test
new file mode 100755
index 0000000..55cfb82
--- /dev/null
+++ b/agen5/test/reorder.test
@@ -0,0 +1,107 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# reorder.test --- test reorder functionality
+#
+# Time-stamp: "2010-06-26 16:04:32 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+cat > $testname.tpl <<'_EOF_'
+<= AutoGen5 template test =>
+From zero:<=
+
+FOR a (for-from 0) (for-by 1) (for-sep ",")=>
+<=(sprintf "%5d: " (for-index))=><=
+ ?% elt %s absent =><=
+ IF (first-for?) => first loop<= ENDIF =><=
+ IF (last-for? ) => last loop<= ENDIF =><=
+ENDFOR =>
+
+From start by two:<=
+
+FOR a (for-by 2) (for-sep ",")=>
+<=(sprintf "%5d: " (for-index)) =><=
+ ?% elt %s absent =><=
+ IF (first-for?) => first loop<= ENDIF =><=
+ IF (last-for? ) => last loop<= ENDIF =><=
+ENDFOR =>
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+cat > $testname.def <<_EOF_
+autogen definitions $testname;
+a[ 3] = { elt = three; };
+a[ 5] = { elt = five; };
+a[12] = { elt = twelve; };
+a[ 8] = { elt = eight; };
+a[ 1] = { elt = one; };
+a[ 2] = { elt = two; };
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo creating $testname.out
+# this is the output we should expect to see
+cat > $testname.out <<_EOF_
+From zero:
+ 0: absent first loop,
+ 1: one,
+ 2: two,
+ 3: three,
+ 4: absent,
+ 5: five,
+ 6: absent,
+ 7: absent,
+ 8: eight,
+ 9: absent,
+ 10: absent,
+ 11: absent,
+ 12: twelve last loop
+
+From start by two:
+ 1: one first loop,
+ 3: three,
+ 5: five,
+ 7: absent,
+ 9: absent,
+ 11: absent last loop
+_EOF_
+
+run_ag x $testname.def || failure autogen failed
+cmp -s $testname.test $testname.out || failure unexpected output
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of reorder.test
diff --git a/agen5/test/shell.test b/agen5/test/shell.test
new file mode 100755
index 0000000..269f46a
--- /dev/null
+++ b/agen5/test/shell.test
@@ -0,0 +1,165 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# shell.test --- test functionality of switching shells
+#
+# Time-stamp: "2012-05-12 19:56:39 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# The test will verify that a real shell processes the declarations
+# and our weirdo shell handles the template.
+#
+# ----------------------------------------------------------------------
+
+exec 9>&2
+
+. ./defs
+
+${FGREP} '#define SHELL_ENABLED ' ${top_builddir}/config.h > /dev/null 2>&1
+test $? -eq 0 || exit 0
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+cat > ${testname}.c <<- \_EOF_
+ #include <stdio.h>
+ #include <string.h>
+ #include <ctype.h>
+ #define NUL '\0'
+ char buf[ 4096 ];
+ int
+ main( int argc, char** argv )
+ {
+ char *pz;
+ for (;;) {
+ pz = fgets(buf, sizeof(buf),stdin);
+ if (pz == NULL)
+ return 1; /* must get something every time */
+ while (isspace(*pz)) pz++;
+ if (*pz != NUL) break; /* ignore initial blank lines */
+ }
+
+ for (;;) {
+ if (*pz == '#') goto next_line;
+ if (strncmp( pz, "cd ", 3 ) == 0) goto next_line;
+ if ((strncmp( pz, "echo", 4 ) == 0) && isspace( pz[4] )) {
+ pz += 5;
+ while (isspace(*pz)) pz++; /* suppress the 'echo' */
+ }
+ if (*pz == '\0') pz--; /* always force a newline */
+ fputs( pz, stdout );
+ fflush( stdout );
+
+ next_line:
+ pz = fgets(buf, sizeof(buf),stdin);
+ if (pz == NULL)
+ break;
+ while (isspace(*pz)) pz++;
+ }
+ return 0;
+ }
+ _EOF_
+
+compile
+
+# The backslashes are stripped by the here-doc processing
+#
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<- _EOF_
+ <= AutoGen5 template test
+ (setenv "SHELL" "./${testname}") =>
+ <=\` echo SHELL=\$SHELL \`=>
+ Some <=dummy=> text
+ <= FOR foo =>
+ foo[<=(for-index)=>] = <=foo=>
+ raw-shell-str: <=(raw-shell-str (get "foo"))=>
+ shell-str: <=(shell-str (get "foo"))=>
+ sub-shell-str: <=(sub-shell-str (get "foo"))=>
+ <= ENDFOR =>
+ <=\` : This is a final test \`=>
+ _EOF_
+
+echo creating ${testname}.def
+cat >${testname}.def <<- \_EOF_
+ AutoGen Definitions shell.tpl;
+
+ foo = "''foo'' 'foo' \"foo\" `foo` $foo";
+ foo = '\\\'bar\\\' \\"bar\\" \`bar\` \$bar';
+ foo = '\\\\\'BAZ\\\\\' \\\\"BAZ\\\\" \\\`BAZ\\\` \\\$BAZ';
+ dummy = `echo "mumble"`; /* processed with regular shell */
+ _EOF_
+
+# this is the output we should expect to see
+cat > ${testname}.sample <<- \_EOF_
+ SHELL=$SHELL
+ Some "mumble" text
+
+ foo[0] = ''foo'' 'foo' "foo" `foo` $foo
+ raw-shell-str: \'\''foo'\'\'' '\''foo'\'' "foo" `foo` $foo'
+ shell-str: "''foo'' 'foo' \"foo\" `foo` $foo"
+ sub-shell-str: `''foo'' 'foo' "foo" \`foo\` $foo`
+
+ foo[1] = \'bar\' \"bar\" \`bar\` \$bar
+ raw-shell-str: '\'\''bar\'\'' \"bar\" \`bar\` \$bar'
+ shell-str: "\\'bar\\' \\\"bar\\\" \`bar\` \$bar"
+ sub-shell-str: `\\'bar\\' \"bar\" \\\`bar\\\` \$bar`
+
+ foo[2] = \\'BAZ\\' \\"BAZ\\" \\`BAZ\\` \\$BAZ
+ raw-shell-str: '\\'\''BAZ\\'\'' \\"BAZ\\" \\`BAZ\\` \\$BAZ'
+ shell-str: "\\\\'BAZ\\\\' \\\\\"BAZ\\\\\" \\\`BAZ\\\` \\\$BAZ"
+ sub-shell-str: `\\\\'BAZ\\\\' \\\"BAZ\\\" \\\\\`BAZ\\\\\` \\\$BAZ`
+
+ : This is a final test
+ _EOF_
+
+run_ag x --shell=$PWD/shell ${testname}.def || \
+ failure "autogen ${testname}.def"
+
+${GREP} -v '^AGexe=' ${testname}.test > ${testname}.XX
+mv -f ${testname}.XX ${testname}.test
+if cmp -s ${testname}.test ${testname}.sample
+then cleanup
+ exit 0
+fi
+
+if ${FGREP} 'SHELL=' ${testname}.test > /dev/null 2>&1
+then
+ if ${FGREP} 'SHELL=$SHELL' ${testname}.test > /dev/null 2>&1
+ then : ; else
+ cat >&9 <<- _EOF_
+ The ${testname}.test output file does not start with "SHELL=\$SHELL"
+ This is because you have a Guile library that cannot modify
+ the environment. This is a known breakage on some platforms
+ (viz., BSD derivatives). Sorry.
+ _EOF_
+ ${FGREP} 'SHELL=' ${testname}.test >&9
+ cleanup
+ exit 0
+ fi
+fi
+
+failure "`set +x;diff -c ${testname}.sample ${testname}.test`"
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of shell.test
diff --git a/agen5/test/snarf.test b/agen5/test/snarf.test
new file mode 100755
index 0000000..c03e408
--- /dev/null
+++ b/agen5/test/snarf.test
@@ -0,0 +1,267 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# snarf.test --- test the extraction of scm-type definitions
+#
+# Time-stamp: "2012-03-04 19:48:23 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # SOURCE FILE # # # # # # # # #
+
+echo creating ${testname}.c
+cat > ${testname}.c <<_EOF_
+#include "${testname}.h"
+#include "${testname}.ini"
+/*=gfunc test_to_example_x
+ *
+ * exparg: in, test input arg desc, optional, list
+ *
+=*/
+SCM
+test_scm_test_to_example_x(SCM in)
+{
+ return in;
+}
+
+/*=symbol mumble_check
+ *
+ * init_val: SCM_BOOL_T
+=*/
+/*=symbol bumble_it
+ *
+ * const_val: 100L
+ * global:
+=*/
+/*=syntax guile_syntax_ele
+ *
+ * type: scm_makacro
+ * cfn: scm_m_undefine
+=*/
+_EOF_
+
+# # # # # # # # # # PROCESS SOURCE FILE # # # # # # # # #
+
+f=`echo ${AGexe} | ${SED} 's/ .*//'`
+
+agsrc=`cd $top_srcdir/agen5 && pwd`
+tplsrc=`cd $top_srcdir/autoopts/tpl && pwd`
+
+cat > ${testname}.cfg <<- _EOF_
+ subblock exparg=arg_name,arg_desc,arg_optional,arg_list
+ template snarf.tpl
+ srcfile
+ assign group = ${testname}_grp
+ assign init = Chosen_init
+ base-name ${testname}
+ agarg -L$agsrc
+ agarg -L$tplsrc
+ input ${testname}.c
+ autogen ${f}
+ _EOF_
+unset DEBUG_ENABLED
+
+echo "getdefs load=${testname}.cfg ${testname}.c"
+${VERBOSE} && {
+ AUTOGEN_TRACE=everything
+ AUTOGEN_TRACE_OUT=">>${testname}-ag-log.txt"
+ export AUTOGEN_TRACE AUTOGEN_TRACE_OUT
+}
+${GDexe} load=${testname}.cfg || \
+ failure getdefs load=${testname}.cfg
+
+${SED} -e "${sed_omit_license}" -e '/^#undef *NEW_PROC *$/,$d' \
+ ${testname}.ini > ${testname}.ini.tst1
+
+${SED} "${sed_omit_license}" ${testname}.h > ${testname}.h.tst1
+
+# # # # # # # # # # EXPECTED INI FILE # # # # # # # # #
+
+echo creating ${testname}.ini.OK1
+cat > ${testname}.ini.OK1 <<'_EOF_'
+#include "snarf.h"
+typedef SCM (*scm_callback_t)(void);
+void Chosen_init(void);
+
+extern SCM snarf_grp_scm_sym_bumble_it = SCM_BOOL_F;
+static SCM snarf_grp_scm_sym_mumble_check = SCM_BOOL_F;
+#if GUILE_VERSION >= 108000
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ scm_c_define_gsubr((char*)(_As), \
+ _Ar, _Ao, _Ax, (scm_callback_t)(void*)ag_scm_ ## _An)
+#else
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ gh_new_procedure((char*)(_As), (scm_callback_t)(void*)ag_scm_ ## _An, \
+ _Ar, _Ao, _Ax)
+#endif
+
+/*
+ * snarf_grp Initialization procedure.
+ */
+void
+Chosen_init(void)
+{
+static char const g_nm[55] =
+/* 0 */ "test->example!\0"
+/* 15 */ "guile-syntax-ele\0"
+/* 32 */ "bumble-it\0"
+/* 42 */ "mumble-check";
+
+ NEW_PROC(g_nm + 0, 0, 0, 1, test_to_example_x);
+ scm_make_synt(g_nm+15, scm_makacro, scm_m_undefine);
+ snarf_grp_scm_sym_bumble_it = scm_permanent_object(SCM_CAR (scm_intern0 (g_nm+32)));
+ snarf_grp_scm_sym_mumble_check = scm_permanent_object(SCM_CAR (scm_intern0 (g_nm+42)));
+}
+_EOF_
+
+cmp ${testname}.ini.tst1 ${testname}.ini.OK1 || \
+ failure "`diff ${testname}.ini.tst1 ${testname}.ini.OK1`"
+
+# # # # # # # # # # EXPECTED HEADER FILE # # # # # # #
+
+echo creating ${testname}.h.OK
+cat > ${testname}.h.OK <<_EOF_
+#ifndef GUILE_PROCS_SNARF_H_GUARD
+#define GUILE_PROCS_SNARF_H_GUARD 1
+#if GUILE_VERSION >= 108000
+# include <libguile.h>
+#else
+# include <guile/gh.h>
+#endif
+
+typedef enum {
+ GH_TYPE_UNDEFINED = 0,
+ GH_TYPE_BOOLEAN,
+ GH_TYPE_SYMBOL,
+ GH_TYPE_CHAR,
+ GH_TYPE_VECTOR,
+ GH_TYPE_PAIR,
+ GH_TYPE_NUMBER,
+ GH_TYPE_STRING,
+ GH_TYPE_PROCEDURE,
+ GH_TYPE_LIST,
+ GH_TYPE_INEXACT,
+ GH_TYPE_EXACT
+} teGuileType;
+
+extern SCM snarf_grp_scm_test_to_example_x(SCM);
+extern SCM snarf_grp_scm_sym_bumble_it;
+
+#endif /* GUILE_PROCS_SNARF_H_GUARD */
+_EOF_
+
+cmp ${testname}.h.* || \
+ failure "`diff ${testname}.h.*`"
+
+# # # # # # # # # # PROCESS SOURCE FILE AGAIN # # # # # # # # #
+
+cp ${testname}.cfg ${testname}.cfg1
+
+echo 'assign debug-enabled = true' >> ${testname}.cfg
+DEBUG_ENABLED=true
+export DEBUG_ENABLED
+
+${GDexe} load=${testname}.cfg || \
+ failure getdefs load=${testname}.cfg
+
+${SED} -e "${sed_omit_license}" \
+ -e '/^#undef *NEW_PROC$/,$d' \
+ ${testname}.ini > ${testname}.ini.tst2
+
+${SED} "${sed_omit_license}" ${testname}.h > ${testname}.h.tst2
+
+# # # # # # # # # # EXPECTED INI FILE # # # # # # # # #
+
+echo creating ${testname}.ini.OK2
+cat > ${testname}.ini.OK2 <<'_EOF_'
+#include "snarf.h"
+typedef SCM (*scm_callback_t)(void);
+void Chosen_init(void);
+
+extern SCM snarf_grp_scm_sym_bumble_it = SCM_BOOL_F;
+static SCM snarf_grp_scm_sym_mumble_check = SCM_BOOL_F;
+#ifdef DEBUG_ENABLED
+static SCM
+agrelay_scm_test_to_example_x(SCM scm0)
+{
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) {
+ static char const proc_z[] =
+ "Called ag_scm_test_to_example_x()\n";
+ fwrite(proc_z, sizeof(proc_z) - 1, 1, trace_fp);
+ }
+ return ag_scm_test_to_example_x(scm0);
+}
+
+#if GUILE_VERSION >= 108000
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ scm_c_define_gsubr((char*)(_As), \
+ _Ar, _Ao, _Ax, (scm_callback_t)(void*)agrelay_scm_ ## _An)
+#else
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ gh_new_procedure((char*)(_As), (scm_callback_t)(void*)agrelay_scm_ ## _An, \
+ _Ar, _Ao, _Ax)
+#endif
+
+#else /* DEBUG_ENABLED *not* */
+#if GUILE_VERSION >= 108000
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ scm_c_define_gsubr((char*)(_As), \
+ _Ar, _Ao, _Ax, (scm_callback_t)(void*)ag_scm_ ## _An)
+#else
+#define NEW_PROC(_As, _Ar, _Ao, _Ax, _An) \
+ gh_new_procedure((char*)(_As), (scm_callback_t)(void*)ag_scm_ ## _An, \
+ _Ar, _Ao, _Ax)
+#endif
+#endif /* DEBUG_ENABLED */
+
+/*
+ * snarf_grp Initialization procedure.
+ */
+void
+Chosen_init(void)
+{
+static char const g_nm[55] =
+/* 0 */ "test->example!\0"
+/* 15 */ "guile-syntax-ele\0"
+/* 32 */ "bumble-it\0"
+/* 42 */ "mumble-check";
+
+ NEW_PROC(g_nm + 0, 0, 0, 1, test_to_example_x);
+ scm_make_synt(g_nm+15, scm_makacro, scm_m_undefine);
+ snarf_grp_scm_sym_bumble_it = scm_permanent_object(SCM_CAR (scm_intern0 (g_nm+32)));
+ snarf_grp_scm_sym_mumble_check = scm_permanent_object(SCM_CAR (scm_intern0 (g_nm+42)));
+}
+_EOF_
+
+cmp ${testname}.ini.tst2 ${testname}.ini.OK2 || \
+ failure "`diff ${testname}.ini.tst2 ${testname}.ini.OK2`"
+
+cleanup
+
+##
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of snarf.test
diff --git a/agen5/test/stack.test b/agen5/test/stack.test
new file mode 100755
index 0000000..32317af
--- /dev/null
+++ b/agen5/test/stack.test
@@ -0,0 +1,93 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# stack.test --- test stack and join functionality
+#
+## Time-stamp: "2010-02-24 08:41:38 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[= AutoGen5 template test =]
+[=
+
+ (join ", " (stack "foo.bar.baz"))
+
+=]
+[=
+
+ (join ",\n" "one" "two" "three" "four" )
+
+=]
+[=
+
+ (join ", " "foo" (stack "foo.bar.baz") "bar" "baz")
+
+=]
+[= (string-substitute (join "\n" (stack "foo.bar.baz"))
+ '("umb" "le") '("_UMB_" "<=") ) =]
+_EOF_
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating ${testname}.def
+cat > ${testname}.def <<_EOF_
+AutoGen definitions ${testname};
+
+foo = { bar = { baz = fumble; }; };
+foo = { bar = { baz = mumble; }; };
+foo = { bar = { baz = grumble; }; };
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.out
+# this is the output we should expect to see
+cat > ${testname}.out <<'_EOF_'
+fumble, mumble, grumble
+one,
+two,
+three,
+four
+foo, fumble, mumble, grumble, bar, baz
+f_UMB_<=
+m_UMB_<=
+gr_UMB_<=
+_EOF_
+
+run_ag x ${testname}.def || failure autogen failed
+cmp -s ${testname}.test ${testname}.out || \
+ failure "`diff ${testname}.*t`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of stack.test
diff --git a/agen5/test/stress.test b/agen5/test/stress.test
new file mode 100755
index 0000000..05dc399
--- /dev/null
+++ b/agen5/test/stress.test
@@ -0,0 +1,68 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# stress.test --- stress test
+#
+## Time-stamp: "2011-03-06 15:26:25 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[= AutoGen5 template test =]
+Entry Count = [= (count "entry") =].
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.samp
+ecount=${STRESS_COUNT:-1000}
+# this is the output we should expect to see
+echo "Entry Count = ${ecount}." > ${testname}.samp
+
+(
+ set +x
+ echo "AutoGen Definitions ${testname};"
+ idx=1
+ while test $idx -le ${ecount}
+ do
+ echo "entry = { value = 'val-${idx}'; depth = '${idx}'; const = xx; };"
+ idx=`expr $idx + 1`
+ done
+) | run_ag x -b ${testname} -
+test $? -eq 0 || failure autogen failed
+
+cmp -s ${testname}.test ${testname}.samp || \
+ failure "`diff ${testname}.test ${testname}.samp`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of stress.test
diff --git a/agen5/test/string.test b/agen5/test/string.test
new file mode 100755
index 0000000..dfcabb3
--- /dev/null
+++ b/agen5/test/string.test
@@ -0,0 +1,247 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# string.test --- test string formation rules
+#
+# Time-stamp: "2012-05-12 19:56:52 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# There are five different things we need to examine:
+#
+# 1. That the autogen internal string.
+# 2. What we expect that string to contain.
+# 3. What is generated as the "C" representation
+# 4. What is generated for raw shell strings
+# 5. What is generated for "cooked" shell strings
+#
+# We will compare all these things by generating a C program that
+# will test the various strings and a shell script to invoke the
+# program with the two shell string formats for arguments.
+# The program will also write out the expected string value.
+# That value will be compared with what autogen wrote out
+# as its internal value.
+#
+# All this stuff must be generated carefully.
+# Specifically, the '${testname}' expressions need to
+# be expanded in certain parts of the output file.
+# In those areas, the eof marker must *not* be quoted.
+# In other places (e.g., where defining the strings),
+# rather than hassle with understanding shell quoting rules,
+# instead we *will* quote the EOF marker to avoid
+# any shell interpretation at all!!
+#
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating $testname.tpl
+exec 4> $testname.tpl
+cat >&4 <<_EOF_
+[= AutoGen5 Template c sh =]
+[=
+
+CASE (suffix) =][=
+
+ == c
+
+=]#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif[=
+
+
+ ;; Create a file containing nothing but the
+ ;; autogen internal contents of the string
+ ;;
+ (out-push-new "${testname}.raw")
+
+ =][=string=][=
+
+ (out-pop)
+
+=]
+_EOF_
+
+test -z "$LINENO" && LINENO=`
+ ${GREP} -n FIND-THIS-LINE-NUMBER $0 | sed 's/:.*//'` # close enough
+printf '\nchar zTestFile[] = "%s";\n#line %s\n' \
+ ${testname}.raw `expr $LINENO + 4` >&4
+
+cat >&4 <<'_EOF_'
+char zGened[] = [=(c-string (get "string"))=];
+char zKrGen[] = [=(kr-string (get "string"))=];
+char zExpect[] = "'\f\r\b\v\t\a\n\n"
+ "\\f\\r\\b\\v\\t\\a\\n\n"
+ "\"Wow!\" This'll be \\hard\\'\n"
+ "#endif /* .\n"
+ "and it'll be a \"hassle\"."
+ "\001\002\003\n'";
+#define expectSize ((int)(sizeof(zExpect) - 1))
+int checkStr( char* pz, char const* pzWhat );
+int checkStr( char* pz, char const* pzWhat )
+{
+ static char const zNotMatch[] =
+ "%s generated string mismatches at offset %d of %d\n"
+ "Expected char: 0x%02X saw char: 0x%02X\n"
+ "Expected string:\n==>%s<==\n\n"
+ "Generated string:\n-->%s<--\n\n";
+
+ char* pzE = zExpect;
+ char* pzR = pz;
+ int ix = strlen( pz );
+ int res = 0;
+
+ if (ix != expectSize) {
+ fprintf( stderr, "%s is %d bytes, not %d\n", pzWhat, ix, expectSize );
+ res = 1;
+ }
+
+ for (ix = 0; ix < expectSize; ix++) {
+ if (*(pzE++) != *(pzR++)) {
+ fprintf(stderr, zNotMatch, pzWhat, ix, expectSize,
+ (unsigned)pzE[-1], (unsigned)pzR[-1], zExpect, pz);
+ return 1;
+ }
+ }
+ if (*pzE != '\0') {
+ fputs( "compile error: expected string too long\n", stderr);
+ res = 1;
+ } else if (*pzR != '\0') {
+ fprintf(stderr, "%s has %d residual characters:\n==>%s<==\n",
+ pzWhat, (int)strlen(pzR), pzR);
+ res = 1;
+ }
+ return res;
+}
+
+
+int main( int argc, char** argv )
+{
+ int resCode = 0;
+
+ /*
+ * Write out the expected value to a file.
+ * The "cmp" program will compare it with the
+ * internal version autogen wrote out itself.
+ */
+ write( STDOUT_FILENO, zExpect, sizeof( zExpect )-1);
+ close( STDOUT_FILENO );
+
+ if (sizeof( zGened ) != sizeof( zExpect )) {
+ fputs( "Expected and generated string sizes do not match.\n",
+ stderr );
+ resCode = 1;
+ }
+
+ if (strlen( zGened ) != sizeof( zGened )-1) {
+ fputs( "The generated string contains a NUL.\n", stderr );
+ resCode++;
+ }
+
+ if (checkStr( zGened, "'C' program" ))
+ resCode++;
+
+ if (checkStr( zKrGen, "K&R 'C' program" ))
+ resCode++;
+
+ if (checkStr( argv[1], "Raw shell" ))
+ resCode++;
+
+ if (checkStr( argv[2], "Cooked shell" ))
+ resCode++;
+
+ return resCode;
+}[=
+
+ == sh
+
+=]#! /bin/sh
+set -x
+_EOF_
+
+cat 1>&4 <<_EOF_
+./${testname} [=(raw-shell-str (get "string"))
+ =] [=(shell-str (get "string"))=] > ${testname}.out
+res=\$?
+cmp ${testname}.out ${testname}.raw > /dev/null 2>&1
+
+if [ \$? -ne 0 ]
+then
+ echo the AutoGen internal content did not match expectations
+ res=\`expr \$res + 1\`
+fi
+if [ \$res -eq 0 ]
+then
+ echo All string comparisons pass
+else
+ echo There were \$res string test failures
+ exit \$res
+fi[=
+
+ESAC
+
+=]
+_EOF_
+exec 4>&-
+
+# # # # # # # # # # DEFINITIONS FILE # # # # # # # # #
+
+echo creating $testname.def
+echo "autogen definitions $testname.tpl;" > $testname.def
+cat >> $testname.def <<'_EOF_'
+
+string =
+ "'\f\r\b\v\t\a\n
+"
+ '\f\r\b\v\t\a\n
+'
+ '"Wow!" This\'ll be \\hard\\\'
+\#endif /* .
+'
+ "and it'll be a \"hassle\"."
+ "\001\x02\X03\n'";
+
+_EOF_
+
+# # # # # # # # # # RUN THE TESTS # # # # # # # # # # #
+
+SHELL=${SHELL-/bin/sh}
+
+run_ag x $testname.def || failure autogen failed
+
+compile
+
+chmod +x *.sh
+
+./${testname}.sh || failure strings do not match
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of string.test
diff --git a/agen5/test/strtable.test b/agen5/test/strtable.test
new file mode 100755
index 0000000..8c06e44
--- /dev/null
+++ b/agen5/test/strtable.test
@@ -0,0 +1,97 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# strtable.test --- test string-table functionality
+#
+# Time-stamp: "2012-01-07 09:02:42 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[= AutoGen5 template test =]
+[=
+
+(string-table-new "scribble")
+(out-push-new) ;; redirect output to temporary
+(define ct 1)
+
+=][=
+
+FOR str IN that was the week that was
+
+=][= (set! ct (+ ct 1)) =]
+ [= (string-table-add-ref "scribble" (get "str")) =],[=
+
+ENDFOR
+
+=][=
+ (out-suspend "main")
+ (emit-string-table "scribble")
+ (emit (sprintf "\n#define STRING_CT %d\n" (- ct 1)))
+ (ag-fprintf 0 "\nchar const * const ap[%d] = {" ct)
+ (out-resume "main")
+ (out-pop #t) ;; now dump out the redirected output
+ ;; and finish: =]
+ NULL };
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.base
+# this is the output we should expect to see
+cat > ${testname}.base <<'_EOF_'
+
+static char const scribble[18] =
+/* 0 */ "that\0"
+/* 5 */ "was\0"
+/* 9 */ "the\0"
+/* 13 */ "week";
+
+#define STRING_CT 6
+
+char const * const ap[7] = {
+ scribble+0,
+ scribble+5,
+ scribble+9,
+ scribble+13,
+ scribble+0,
+ scribble+5,
+ NULL };
+_EOF_
+
+run_ag x -b ${testname} -T ${testname}.tpl --no-defin || \
+ failure autogen failed
+cmp -s ${testname}.base ${testname}.test || \
+ failure "bad output: `diff -c ${testname}.base ${testname}.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of strtable.test
diff --git a/agen5/test/strxform.test b/agen5/test/strxform.test
new file mode 100755
index 0000000..cdf043a
--- /dev/null
+++ b/agen5/test/strxform.test
@@ -0,0 +1,87 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# strxform.test --- test string transformation functionality
+#
+# Time-stamp: "2011-02-02 12:02:13 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+#
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+${SED} 's/^ *[0-9]*: //' > ${testname}.tpl <<- _EOTPL_
+ 1: [= AutoGen5 Template test =]
+ 2: string input: [= in-str =]
+ 3:
+ 4: string->c-name! [= (string->c-name! (get "in-str")) =]
+ 5: string-upcase! [= (string-upcase! (get "in-str")) =]
+ 6: string-capitalize! [= (string-capitalize! (get "in-str")) =]
+ 7: string-capitalize [= (string-capitalize (get "in-str")) =]
+ 8: string-downcase! [= (string-downcase! (get "in-str")) =]
+ 9: string-downcase [= (string-downcase (get "in-str")) =]
+10: string->camelcase [= (string->camelcase (get "in-str")) =]
+_EOTPL_
+
+cat > ${testname}.def <<- _EOF_
+ AutoGen Definitions ${testname}.tpl;
+ in-str = "The 10quick\tfoxes-jumped/very=\n=high9indeed!";
+ _EOF_
+
+# # # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+set -x
+echo creating ${testname}.out
+# this is the output we should expect to see
+cat > ${testname}.out <<- \_EOF_
+ string input: The 10quick foxes-jumped/very=
+ =high9indeed!
+
+ string->c-name! The 10quick foxes_jumped_very_
+ _high9indeed_
+ string-upcase! THE 10QUICK FOXES-JUMPED/VERY=
+ =HIGH9INDEED!
+ string-capitalize! The 10quick Foxes-Jumped/Very=
+ =High9indeed!
+ string-capitalize The 10quick Foxes-Jumped/Very=
+ =High9indeed!
+ string-downcase! the 10quick foxes-jumped/very=
+ =high9indeed!
+ string-downcase the 10quick foxes-jumped/very=
+ =high9indeed!
+ string->camelcase The10QuickFoxesJumpedVeryHigh9Indeed
+ _EOF_
+
+run_ag x ${testname}.def || failure autogen failed
+
+cmp -s ${testname}.test ${testname}.out || \
+ failure "unexpected output:${nl}`diff ${testname}.out ${testname}.test`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of strxform.test
diff --git a/agen5/test/suffix.test b/agen5/test/suffix.test
new file mode 100755
index 0000000..55997f5
--- /dev/null
+++ b/agen5/test/suffix.test
@@ -0,0 +1,90 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# suffix.test --- test the select suffix option
+#
+# Time-stamp: "2013-03-10 07:10:42 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<'_EOF_'
+[= AutoGen5 Template test =]
+[=
+
+CASE (suffix) =][=
+
+== test =]BOGUS[=
+== "* NONE *" =]No Suffix[=
+== Example =]Example[=
+* =]Bogon ``[=(suffix)=]''[=
+
+ESAC =]
+_EOF_
+
+# # # # # # # # # # EXPECTED OUTPUT FILE # # # # # # #
+
+echo "Example" > ${testname}.Example.test
+echo "No Suffix" > ${testname}.stdout.test
+
+# # # # # # # # # # RUN TESTS # # # # # # #
+nodefopt="--no-def -T${testname}.tpl"
+run_ag ex ${nodefopt} -oExample -b${testname} || \
+ failure autogen ${nodefopt} -oExample -b${testname}
+
+cmp ${testname}.Example* || \
+ failure `diff -c cmp ${testname}.Example*`
+
+# # # # # # # # # # STDOUT TEMPLATE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<'_EOF_'
+[= AutoGen5 Template =]
+[=
+text =]
+_EOF_
+
+cat > ${testname}.samp <<- EOF
+ This is some text.
+ This should appear in the output.
+EOF
+
+cat > ${testname}.def <<\EOF
+AutoGen Definitions suffix;
+text = `cat suffix.samp`;
+EOF
+
+run_ag sfx ${testname}.def | ${EGREP} -v '^in state ' > ${testname}.out
+cmp ${testname}.out ${testname}.samp || \
+ failure "`diff ${testname}.out ${testname}.samp`"
+
+cleanup
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of suffix.test
diff --git a/agen5/test/time.test b/agen5/test/time.test
new file mode 100755
index 0000000..ad46a38
--- /dev/null
+++ b/agen5/test/time.test
@@ -0,0 +1,64 @@
+#! /bin/sh
+# -*- Mode: Shell-script -*-
+# ----------------------------------------------------------------------
+# time.test --- test modification time settings
+#
+## Time-stamp: "2010-02-24 08:41:34 bkorb"
+# Author: Bruce Korb <bkorb@gnu.org>
+##
+## This file is part of AutoGen.
+## AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+##
+## AutoGen is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## AutoGen is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## See the GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+# ----------------------------------------------------------------------
+
+. ./defs
+
+# # # # # # # # # # TEMPLATE FILE # # # # # # # # #
+
+echo creating ${testname}.tpl
+cat > ${testname}.tpl <<_EOF_
+[= AutoGen5 template test =]
+Plain text template.
+_EOF_
+
+touch -t 200109110846.00 ${testname}.tpl
+
+run_ag time --source-time -b ${testname} -T ${testname}.tpl --no-definitions || \
+ failure autogen failed
+
+touch -t 200109110846.02 ${testname}.taaa
+
+set -- `ls -t ${testname}.t*`
+
+while :
+do
+ test "${3}" = ${testname}.tpl || break
+ test "${2}" = ${testname}.test || break
+ test "${1}" = ${testname}.taaa || break
+ cleanup
+ exit 0
+done
+
+failure "wrong file time ordering: $*"
+
+## Local Variables:
+## mode: shell-script
+## indent-tabs-mode: nil
+## sh-indentation: 2
+## sh-basic-offset: 2
+## End:
+
+# end of time.test
diff --git a/agen5/tpLoad.c b/agen5/tpLoad.c
new file mode 100644
index 0000000..9180662
--- /dev/null
+++ b/agen5/tpLoad.c
@@ -0,0 +1,560 @@
+
+/**
+ * @file tpLoad.c
+ *
+ * Time-stamp: "2012-08-11 08:55:46 bkorb"
+ *
+ * This module will load a template and return a template structure.
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static bool
+read_okay(char const * pzFName);
+
+static char const *
+expand_dir(char const ** dir_pp, char * name_buf);
+
+static size_t
+cnt_macros(char const * pz);
+
+static void
+load_macs(templ_t * pT, char const * pzF, char const * pzN,
+ char const * pzData);
+
+static templ_t *
+digest_tpl(tmap_info_t * minfo, char * fname);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * Return the template structure matching the name passed in.
+ */
+LOCAL templ_t *
+find_tpl(char const * pzTemplName)
+{
+ templ_t * pT = named_tpls;
+ while (pT != NULL) {
+ if (streqvcmp(pzTemplName, pT->td_name) == 0)
+ break;
+ pT = (templ_t*)(void*)(pT->td_scan);
+ }
+ return pT;
+}
+
+/**
+ * the name is a regular file with read access.
+ * @param[in] pzFName file name to check
+ * @returns \a true when the named file exists and is a regular file
+ * @returns \a false otherwise.
+ */
+static bool
+read_okay(char const * pzFName)
+{
+ struct stat stbf;
+ if (stat(pzFName, &stbf) != 0)
+ return false;
+ if (! S_ISREG(stbf.st_mode))
+ return false;
+ return (access(pzFName, R_OK) == 0) ? true : false;
+}
+
+/**
+ * Expand a directory name that starts with '$'.
+ *
+ * @param[in,out] dir_pp pointer to pointer to directory name
+ * @returns the resulting pointer
+ */
+static char const *
+expand_dir(char const ** dir_pp, char * name_buf)
+{
+ char * res = (void *)*dir_pp;
+
+ if (res[1] == NUL)
+ AG_ABEND(aprf(LOAD_FILE_SHORT_NAME, res));
+
+ if (! optionMakePath(name_buf, (int)AG_PATH_MAX, res,
+ autogenOptions.pzProgPath)) {
+ /*
+ * The name expanded to "empty", so substitute curdir.
+ */
+ strcpy(res, FIND_FILE_CURDIR);
+
+ } else {
+ free(res);
+ AGDUPSTR(res, name_buf, "find dir name");
+ *dir_pp = res; /* save computed name for later */
+ }
+
+ return res;
+}
+
+/**
+ * Search for a file.
+ *
+ * Starting with the current directory, search the directory list trying to
+ * find the base template file name. If there is a referring template (a
+ * template with an "INCLUDE" macro), then try that, too, before giving up.
+ *
+ * @param[in] in_name the file name we are looking for.
+ * @param[out] res_name where we stash the file name we found.
+ * @param[in] sfx_list a list of suffixes to try, if \a in_name has none.
+ * @param[in] referring_tpl file name of the template with a INCLUDE macro.
+ *
+ * @returns \a SUCCESS when \a res_name is valid
+ * @returns \a FAILURE when the file is not found.
+ */
+LOCAL tSuccess
+find_file(char const * in_name,
+ char * res_name,
+ char const * const * sfx_list,
+ char const * referring_tpl)
+{
+ bool no_suffix;
+ void * free_me = NULL;
+ tSuccess res = SUCCESS;
+
+ size_t nm_len = strlen(in_name);
+ if (nm_len >= AG_PATH_MAX - MAX_SUFFIX_LEN)
+ return FAILURE;
+
+ /*
+ * Expand leading environment variables.
+ * We will not mess with embedded ones.
+ */
+ if (*in_name == '$') {
+ if (! optionMakePath(res_name, (int)AG_PATH_MAX, in_name,
+ autogenOptions.pzProgPath))
+ return FAILURE;
+
+ AGDUPSTR(in_name, res_name, "find file name");
+ free_me = (void*)in_name;
+
+ /*
+ * in_name now points to the name the file system can use.
+ * It must _not_ point to res_name because we will likely
+ * rewrite that value using this pointer!
+ */
+ nm_len = strlen(in_name);
+ }
+
+ /*
+ * Not a complete file name. If there is not already
+ * a suffix for the file name, then append ".tpl".
+ * Check for immediate access once again.
+ */
+ {
+ char * bf = strrchr(in_name, '/');
+ bf = (bf != NULL) ? strchr(bf, '.') : strchr(in_name, '.');
+ no_suffix = (bf == NULL);
+ }
+
+ /*
+ * The referrer is useful only if it includes a directory name.
+ * If not NULL, referring_tpl becomes an allocated directory name.
+ */
+ if (referring_tpl != NULL) {
+ char * pz = strrchr(referring_tpl, '/');
+ if (pz == NULL)
+ referring_tpl = NULL;
+ else {
+ AGDUPSTR(referring_tpl, referring_tpl, "referring_tpl");
+ pz = strrchr(referring_tpl, '/');
+ *pz = NUL;
+ }
+ }
+
+ {
+ /*
+ * Search each directory in our directory search list for the file.
+ * We always force two copies of this option, so we know it exists.
+ * Later entries are more recently added and are searched first.
+ * We start the "dirlist" pointing to the real last entry.
+ */
+ int ct = STACKCT_OPT(TEMPL_DIRS);
+ char const ** dirlist = STACKLST_OPT(TEMPL_DIRS) + ct - 1;
+ char const * c_dir = FIND_FILE_CURDIR;
+
+ /*
+ * IF the file name starts with a directory separator,
+ * then we only search once, looking for the exact file name.
+ */
+ if (*in_name == '/')
+ ct = -1;
+
+ for (;;) {
+ char * pzEnd;
+
+ /*
+ * c_dir is always FIND_FILE_CURDIR the first time through
+ * and is never that value after that.
+ */
+ if (c_dir == FIND_FILE_CURDIR) {
+
+ memcpy(res_name, in_name, nm_len);
+ pzEnd = res_name + nm_len;
+ *pzEnd = NUL;
+
+ } else {
+ int fmt_len;
+
+ /*
+ * IF one of our template paths starts with '$', then expand it
+ * and replace it now and forever (the rest of this run, anyway).
+ */
+ if (*c_dir == '$')
+ c_dir = expand_dir(dirlist+1, res_name);
+
+ fmt_len = snprintf(res_name, AG_PATH_MAX - MAX_SUFFIX_LEN,
+ FIND_FILE_DIR_FMT, c_dir, in_name);
+ if (fmt_len >= AG_PATH_MAX - MAX_SUFFIX_LEN)
+ break; // fail-return
+ pzEnd = res_name + fmt_len;
+ }
+
+ if (read_okay(res_name))
+ goto find_file_done;
+
+ /*
+ * IF the file does not already have a suffix,
+ * THEN try the ones that are okay for this file.
+ */
+ if (no_suffix && (sfx_list != NULL)) {
+ char const * const * sfxl = sfx_list;
+ *(pzEnd++) = '.';
+
+ do {
+ strcpy(pzEnd, *(sfxl++)); /* must fit */
+ if (read_okay(res_name))
+ goto find_file_done;
+
+ } while (*sfxl != NULL);
+ }
+
+ /*
+ * IF we've exhausted the search list,
+ * THEN see if we're done, else go through search dir list.
+ *
+ * We try one more thing if there is a referrer.
+ * If the searched-for file is a full path, "ct" will
+ * start at -1 and we will leave the loop here and now.
+ */
+ if (--ct < 0) {
+ if ((referring_tpl == NULL) || (ct != -1))
+ break;
+ c_dir = referring_tpl;
+
+ } else {
+ c_dir = *(dirlist--);
+ }
+ }
+ }
+
+ res = FAILURE;
+
+ find_file_done:
+ AGFREE(free_me);
+ AGFREE(referring_tpl);
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Count the macros in a template.
+ * We need to allocate the right number of pointers.
+ */
+static size_t
+cnt_macros(char const * pz)
+{
+ size_t ct = 2;
+ for (;;) {
+ pz = strstr(pz, st_mac_mark);
+ if (pz == NULL)
+ break;
+ ct += 2;
+ if (strncmp(pz - end_mac_len, end_mac_mark, end_mac_len) == 0)
+ ct--;
+ pz += st_mac_len;
+ }
+ return ct;
+}
+
+
+/**
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Load the macro array and file name.
+ */
+static void
+load_macs(templ_t * pT, char const * pzF, char const * pzN,
+ char const * pzData)
+{
+ macro_t* pMac = pT->td_macros;
+
+ {
+ char* pzText = (char*)(pMac + pT->td_mac_ct);
+ size_t len;
+
+ AGDUPSTR(pT->td_file, pzF, "templ file");
+
+ len = strlen(pzN) + 1;
+ memcpy((void*)pzText, (void*)pzN, len);
+ pT->td_name = pzText;
+ pzText += len;
+ pT->td_text = pzText;
+ pT->td_scan = pzText + 1;
+ }
+
+ current_tpl = pT;
+
+ {
+ macro_t* pMacEnd = parse_tpl(pMac, &pzData);
+ int ct;
+
+ /*
+ * Make sure all of the input string was scanned.
+ */
+ if (pzData != NULL)
+ AG_ABEND(LOAD_MACS_BAD_PARSE);
+
+ ct = pMacEnd - pMac;
+
+ /*
+ * IF there are empty macro slots,
+ * THEN pack the text
+ */
+ if (ct < pT->td_mac_ct) {
+ int delta = sizeof(macro_t) * (pT->td_mac_ct - ct);
+ void* data =
+ (pT->td_name == NULL) ? pT->td_text : pT->td_name;
+ size_t size = pT->td_scan - (char*)data;
+ memmove((void*)pMacEnd, data, size);
+
+ pT->td_text -= delta;
+ pT->td_scan -= delta;
+ pT->td_name -= delta;
+ pT->td_mac_ct = ct;
+ }
+ }
+
+ pT->td_size = pT->td_scan - (char*)pT;
+ pT->td_scan = NULL;
+
+ /*
+ * We cannot reallocate a smaller array because
+ * the entries are all linked together and
+ * realloc-ing it may cause it to move.
+ */
+#if defined(DEBUG_ENABLED)
+ if (HAVE_OPT(SHOW_DEFS)) {
+ static char const zSum[] =
+ "loaded %d macros from %s\n"
+ "\tBinary template size: 0x%zX\n\n";
+ fprintf(trace_fp, zSum, pT->td_mac_ct, pzF, pT->td_size);
+ }
+#endif
+}
+
+/**
+ * Load a template from mapped memory. Load up the pseudo macro,
+ * count the macros, allocate the data, and parse all the macros.
+ *
+ * @param[in] minfo information about the mapped memory.
+ * @param[in] fname the full path input file name.
+ *
+ * @returns the digested data
+ */
+static templ_t *
+digest_tpl(tmap_info_t * minfo, char * fname)
+{
+ templ_t * res;
+
+ /*
+ * Count the number of macros in the template. Compute
+ * the output data size as a function of the number of macros
+ * and the size of the template data. These may get reduced
+ * by comments.
+ */
+ char const * dta =
+ loadPseudoMacro((char const *)minfo->txt_data, fname);
+
+ size_t mac_ct = cnt_macros(dta);
+ size_t alloc_sz = (sizeof(*res) + (mac_ct * sizeof(macro_t))
+ + minfo->txt_size
+ - (dta - (char const *)minfo->txt_data)
+ + strlen(fname) + 0x10) & ~0x0F;
+
+ res = (templ_t*)AGALOC(alloc_sz, "main template");
+ memset((void*)res, 0, alloc_sz);
+
+ /*
+ * Initialize the values:
+ */
+ res->td_magic = magic_marker;
+ res->td_size = alloc_sz;
+ res->td_mac_ct = mac_ct;
+
+ strcpy(res->td_start_mac, st_mac_mark); /* must fit */
+ strcpy(res->td_end_mac, end_mac_mark); /* must fit */
+ load_macs(res, fname, PSEUDO_MAC_TPL_FILE, dta);
+
+ res->td_name -= (long)res;
+ res->td_text -= (long)res;
+ res = (templ_t*)AGREALOC((void*)res, res->td_size,
+ "resize template");
+ res->td_name += (long)res;
+ res->td_text += (long)res;
+
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Starting with the current directory, search the directory
+ * list trying to find the base template file name.
+ */
+LOCAL templ_t *
+tpl_load(char const * fname, char const * referrer)
+{
+ static tmap_info_t map_info;
+ static char tpl_file[ AG_PATH_MAX ];
+
+ /*
+ * Find the template file somewhere
+ */
+ {
+ static char const * const sfx_list[] = {
+ LOAD_TPL_SFX_TPL, LOAD_TPL_SFX_AGL, NULL };
+ if (! SUCCESSFUL(find_file(fname, tpl_file, sfx_list, referrer))) {
+ errno = ENOENT;
+ AG_CANT(LOAD_TPL_CANNOT_MAP, fname);
+ }
+ }
+
+ /*
+ * Make sure the specified file is a regular file.
+ * Make sure the output time stamp is at least as recent.
+ */
+ {
+ struct stat stbf;
+ if (stat(tpl_file, &stbf) != 0)
+ AG_CANT(LOAD_TPL_CANNOT_STAT, fname);
+
+ if (! S_ISREG(stbf.st_mode)) {
+ errno = EINVAL;
+ AG_CANT(LOAD_TPL_IRREGULAR, fname);
+ }
+
+ if (outfile_time < stbf.st_mtime)
+ outfile_time = stbf.st_mtime;
+ }
+
+ text_mmap(tpl_file, PROT_READ|PROT_WRITE, MAP_PRIVATE, &map_info);
+ if (TEXT_MMAP_FAILED_ADDR(map_info.txt_data))
+ AG_ABEND(aprf(LOAD_TPL_CANNOT_OPEN, tpl_file));
+
+ if (dep_fp != NULL)
+ add_source_file(tpl_file);
+
+ /*
+ * Process the leading pseudo-macro. The template proper
+ * starts immediately after it.
+ */
+ {
+ macro_t * sv_mac = cur_macro;
+ templ_t * res;
+ cur_macro = NULL;
+
+ res = digest_tpl(&map_info, tpl_file);
+ cur_macro = sv_mac;
+ text_munmap(&map_info);
+
+ return res;
+ }
+}
+
+/**
+ * Deallocate anything related to a template.
+ * This includes the pointer passed in and any macros that have an
+ * unload procedure associated with it.
+ *
+ * @param[in] tpl the template to unload
+ */
+LOCAL void
+tpl_unload(templ_t * tpl)
+{
+ macro_t * mac = tpl->td_macros;
+ int ct = tpl->td_mac_ct;
+
+ while (--ct >= 0) {
+ unload_proc_p_t proc;
+ unsigned int ix = mac->md_code;
+
+ /*
+ * "select" functions get remapped, depending on the alias used for
+ * the selection. See the "mac_func_t" enumeration in functions.h.
+ */
+ if (ix >= FUNC_CT)
+ ix = FTYP_SELECT;
+
+ proc = unload_procs[ ix ];
+ if (proc != NULL)
+ (*proc)(mac);
+
+ mac++;
+ }
+
+ AGFREE((void*)(tpl->td_file));
+ AGFREE(tpl);
+}
+
+/**
+ * This gets called when all is well at the end.
+ * The supplied template and all named templates are unloaded.
+ *
+ * @param[in] tpl the last template standing
+ */
+LOCAL void
+cleanup(templ_t * tpl)
+{
+ if (HAVE_OPT(USED_DEFINES))
+ print_used_defines();
+
+ if (dep_fp != NULL)
+ wrap_up_depends();
+
+ optionFree(&autogenOptions);
+
+ for (;;) {
+ tpl_unload(tpl);
+ tpl = named_tpls;
+ if (tpl == NULL)
+ break;
+ named_tpls = (templ_t*)(void*)(tpl->td_scan);
+ }
+
+ free_for_context(true);
+ unload_defs();
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/tpLoad.c */
diff --git a/agen5/tpParse.c b/agen5/tpParse.c
new file mode 100644
index 0000000..8f053c6
--- /dev/null
+++ b/agen5/tpParse.c
@@ -0,0 +1,400 @@
+
+/**
+ * @file tpParse.c
+ *
+ * Time-stamp: "2012-04-22 10:05:41 bkorb"
+ *
+ * This module will load a template and return a template structure.
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if defined(DEBUG_ENABLED)
+static int tplNestLevel = 0;
+
+static char const zTDef[] = "%-10s (%d) line %d end=%d, strlen=%d\n";
+#endif
+
+/* = = = START-STATIC-FORWARD = = = */
+static mac_func_t
+func_code(char const ** ppzScan);
+
+static char const *
+find_mac_end(char const ** ppzMark);
+
+static char const *
+find_mac_start(char const * pz, macro_t** ppM, templ_t* pTpl);
+
+static char const *
+find_macro(templ_t * pTpl, macro_t ** ppM, char const ** ppzScan);
+/* = = = END-STATIC-FORWARD = = = */
+
+/*
+ * Return the enumerated function type corresponding
+ * to a name pointed to by the input argument.
+ */
+static mac_func_t
+func_code(char const ** ppzScan)
+{
+ fn_name_type_t const * pNT;
+ char const * pzFuncName = *ppzScan;
+ int hi, lo, av;
+ int cmp;
+
+ /*
+ * IF the name starts with a punctuation, then it is some sort of
+ * alias. Find the function in the alias portion of the table.
+ */
+ if (IS_PUNCTUATION_CHAR(*pzFuncName)) {
+ hi = FUNC_ALIAS_HIGH_INDEX;
+ lo = FUNC_ALIAS_LOW_INDEX;
+ do {
+ av = (hi + lo)/2;
+ pNT = fn_name_types + av;
+ cmp = (int)(*(pNT->pName)) - (int)(*pzFuncName);
+
+ /*
+ * For strings that start with a punctuation, we
+ * do not need to test for the end of token
+ * We will not strip off the marker and the load function
+ * will figure out what to do with the code.
+ */
+ if (cmp == 0)
+ return pNT->fType;
+ if (cmp > 0)
+ hi = av - 1;
+ else lo = av + 1;
+ } while (hi >= lo);
+ return FTYP_BOGUS;
+ }
+
+ if (! IS_VAR_FIRST_CHAR(*pzFuncName))
+ return FTYP_BOGUS;
+
+ hi = FUNC_NAMES_HIGH_INDEX;
+ lo = FUNC_NAMES_LOW_INDEX;
+
+ do {
+ av = (hi + lo)/2;
+ pNT = fn_name_types + av;
+ cmp = strneqvcmp(pNT->pName, pzFuncName, (int)pNT->cmpLen);
+ if (cmp == 0) {
+ /*
+ * Make sure we matched to the end of the token.
+ */
+ if (IS_VARIABLE_NAME_CHAR(pzFuncName[pNT->cmpLen]))
+ break;
+
+ /*
+ * Advance the scanner past the macro name.
+ * The name is encoded in the "fType".
+ */
+ *ppzScan = pzFuncName + pNT->cmpLen;
+ return pNT->fType;
+ }
+ if (cmp > 0)
+ hi = av - 1;
+ else lo = av + 1;
+ } while (hi >= lo);
+
+ /*
+ * Save the name for later lookup
+ */
+ cur_macro->md_name_off = (current_tpl->td_scan - current_tpl->td_text);
+ {
+ char * pzCopy = current_tpl->td_scan;
+ char * pe = SPN_VALUE_NAME_CHARS(pzFuncName);
+ size_t l = pe - pzFuncName;
+ memcpy(pzCopy, pzFuncName, l);
+ pzCopy += l;
+ pzFuncName += l;
+
+ /*
+ * Names are allowed to contain colons, but not end with them.
+ */
+ if (pzCopy[-1] == ':')
+ pzCopy--, pzFuncName--;
+
+ *(pzCopy++) = NUL;
+ *ppzScan = pzFuncName;
+ current_tpl->td_scan = pzCopy;
+ }
+
+ /*
+ * "Unknown" means we have to check again before we
+ * know whether to assign it to "FTYP_INVOKE" or "FTYP_COND".
+ * That depends on whether or not we find a named template
+ * at template instantiation time.
+ */
+ return FTYP_UNKNOWN;
+}
+
+static char const *
+find_mac_end(char const ** ppzMark)
+{
+ char const * pzMark = *ppzMark + st_mac_len;
+ char const * pzFunc;
+ char const * pzNextMark;
+ char const * pzEndMark;
+
+ /*
+ * Set our pointers to the start of the macro text
+ */
+ for (;;) {
+ pzMark = SPN_NON_NL_WHITE_CHARS(pzMark);
+ if (*pzMark != NL)
+ break;
+ tpl_line++;
+ pzMark++;
+ }
+
+ pzFunc = pzMark;
+ cur_macro->md_code = func_code(&pzMark);
+ cur_macro->md_line = tpl_line;
+ *ppzMark = pzMark;
+
+ /*
+ * Find the end. (We must.) If the thing is empty, treat as a comment,
+ * but warn about it.
+ */
+ pzEndMark = strstr(pzMark, end_mac_mark);
+ if (pzEndMark == NULL)
+ AG_ABEND(FIND_MAC_END_NOPE);
+
+ if (pzEndMark == pzFunc) {
+ cur_macro->md_code = FTYP_COMMENT;
+ fprintf(trace_fp, FIND_MAC_END_EMPTY,
+ current_tpl->td_file, tpl_line);
+ return pzEndMark;
+ }
+
+ /*
+ * Back up over a preceding backslash. It is a flag to indicate the
+ * removal of the end of line white space.
+ */
+ if (pzEndMark[-1] == '\\')
+ pzEndMark--;
+
+ pzNextMark = strstr(pzMark, st_mac_mark);
+ if (pzNextMark == NULL)
+ return pzEndMark;
+
+ if (pzEndMark > pzNextMark)
+ AG_ABEND(FIND_MAC_END_NESTED);
+
+ return pzEndMark;
+}
+
+static char const *
+find_mac_start(char const * pz, macro_t** ppM, templ_t* pTpl)
+{
+ char * pzCopy;
+ char const * pzEnd;
+ char const * res = strstr(pz, st_mac_mark);
+ macro_t * pM = *ppM;
+
+ if (res == pz)
+ return res;
+
+ /*
+ * There is some text here. Make a text macro entry.
+ */
+ pzCopy = pTpl->td_scan;
+ pzEnd = (res != NULL) ? res : pz + strlen(pz);
+ pM->md_txt_off = pzCopy - pTpl->td_text;
+ pM->md_code = FTYP_TEXT;
+ pM->md_line = tpl_line;
+
+#if defined(DEBUG_ENABLED)
+ if (HAVE_OPT(SHOW_DEFS)) {
+ int ct = tplNestLevel;
+ fprintf(trace_fp, "%3u ", (unsigned int)(pM - pTpl->td_macros));
+ do { fputs(" ", trace_fp); } while (--ct > 0);
+
+ fprintf(trace_fp, zTDef, ag_fun_names[ FTYP_TEXT ], FTYP_TEXT,
+ pM->md_line, pM->md_end_idx, (unsigned int)(pzEnd - pz));
+ }
+#endif
+
+ do {
+ if ((*(pzCopy++) = *(pz++)) == NL)
+ tpl_line++;
+ } while (pz < pzEnd);
+
+ *(pzCopy++) = NUL;
+ *ppM = pM + 1;
+ pTpl->td_scan = pzCopy;
+
+ return res; /* may be NULL, if there are no more macros */
+}
+
+static char const *
+find_macro(templ_t * pTpl, macro_t ** ppM, char const ** ppzScan)
+{
+ char const * pzScan = *ppzScan;
+ char const * pzMark;
+
+ pzMark = find_mac_start(pzScan, ppM, pTpl);
+
+ /*
+ * IF no more macro marks are found, THEN we are done...
+ */
+ if (pzMark == NULL)
+ return pzMark;
+
+ /*
+ * Find the macro code and the end of the macro invocation
+ */
+ cur_macro = *ppM;
+ pzScan = find_mac_end(&pzMark);
+
+ /*
+ * Count the lines in the macro text and advance the
+ * text pointer to after the marker.
+ */
+ {
+ char const * pzMacEnd = pzScan;
+ char const * pz = pzMark;
+
+ for (;;pz++) {
+ pz = strchr(pz, NL);
+ if ((pz == NULL) || (pz > pzMacEnd))
+ break;
+ tpl_line++;
+ }
+
+ /*
+ * Strip white space from the macro
+ */
+ pzMark = SPN_WHITESPACE_CHARS(pzMark);
+
+ if (pzMark != pzMacEnd) {
+ pzMacEnd = SPN_WHITESPACE_BACK( pzMark, pzMacEnd);
+ (*ppM)->md_txt_off = (uintptr_t)pzMark;
+ (*ppM)->md_res = (long)(pzMacEnd - pzMark);
+ }
+ }
+
+ /*
+ * IF the end macro mark was preceded by a backslash, then we remove
+ * trailing white space from there to the end of the line.
+ */
+ if ((*pzScan != '\\') || (strncmp(end_mac_mark, pzScan, end_mac_len) == 0))
+ pzScan += end_mac_len;
+
+ else {
+ char const * pz;
+ pzScan += end_mac_len + 1;
+ pz = SPN_NON_NL_WHITE_CHARS(pzScan);
+ if (*pz == NL) {
+ pzScan = pz + 1;
+ tpl_line++;
+ }
+ }
+
+ *ppzScan = pzScan;
+ return pzMark;
+}
+
+LOCAL macro_t *
+parse_tpl(macro_t * mac, char const ** p_scan)
+{
+ char const * scan = *p_scan;
+ templ_t * tpl = current_tpl;
+
+#if defined(DEBUG_ENABLED)
+ static char const zTUndef[] = "%-10s (%d) line %d - MARKER\n";
+
+ #define DEBUG_DEC(l) l--
+
+ if ( ((tplNestLevel++) > 0)
+ && HAVE_OPT(SHOW_DEFS)) {
+ int ct = tplNestLevel;
+ macro_t* pPm = mac-1;
+
+ fprintf(trace_fp, "%3u ", (unsigned int)(pPm - tpl->td_macros));
+ do { fputs(" ", trace_fp); } while (--ct > 0);
+
+ fprintf(trace_fp, zTUndef, ag_fun_names[ pPm->md_code ],
+ pPm->md_code, pPm->md_line);
+ }
+#else
+ #define DEBUG_DEC(l)
+#endif
+
+ while (find_macro(tpl, &mac, &scan) != NULL) {
+ /*
+ * IF the called function returns a NULL next macro pointer,
+ * THEN some block has completed. The returned scanning pointer
+ * will be non-NULL.
+ */
+ {
+ load_proc_p_t const fn = load_proc_table[mac->md_code];
+ macro_t * nxt_mac = fn(tpl, mac, &scan);
+
+#if defined(DEBUG_ENABLED)
+ if (HAVE_OPT(SHOW_DEFS)) {
+ mac_func_t ft = mac->md_code;
+ int ln = mac->md_line;
+ int ct = tplNestLevel;
+ if (mac->md_code == FTYP_BOGUS)
+ fputs(" ", trace_fp);
+ else fprintf(trace_fp, "%3u ",
+ (unsigned int)(mac - tpl->td_macros));
+
+ do { fputs(" ", trace_fp); } while (--ct > 0);
+
+ if (mac->md_code == FTYP_BOGUS)
+ fprintf(trace_fp, zTUndef, ag_fun_names[ ft ], ft, ln);
+ else {
+ char const * pz;
+ if (ft >= FUNC_CT)
+ ft = FTYP_SELECT;
+ pz = (mac->md_txt_off == 0)
+ ? zNil
+ : (tpl->td_text + mac->md_txt_off);
+ fprintf(trace_fp, zTDef, ag_fun_names[ft], mac->md_code,
+ ln, mac->md_end_idx, (unsigned int)strlen(pz));
+ }
+ }
+#endif
+
+ if (nxt_mac == NULL) {
+ *p_scan = scan;
+ DEBUG_DEC(tplNestLevel);
+ return mac;
+ }
+ mac = nxt_mac;
+ }
+ }
+
+ DEBUG_DEC(tplNestLevel);
+
+ /*
+ * We reached the end of the input string.
+ * Return a NULL scanning pointer and a pointer to the end.
+ */
+ *p_scan = NULL;
+ return mac;
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/tpParse.c */
diff --git a/agen5/tpProcess.c b/agen5/tpProcess.c
new file mode 100644
index 0000000..4eb4f6f
--- /dev/null
+++ b/agen5/tpProcess.c
@@ -0,0 +1,413 @@
+
+/**
+ * @file tpProcess.c
+ *
+ * Parse and process the template data descriptions
+ *
+ * Time-stamp: "2012-04-07 09:26:41 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+trace_macro(templ_t * tpl, macro_t * mac);
+
+static void
+do_stdout_tpl(templ_t * tpl);
+
+static void
+open_output(out_spec_t * spec);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * Generate all the text within a block.
+ * The caller must know the exact bounds of the block.
+ *
+ * @param tpl template containing block of macros
+ * @param mac first macro in series
+ * @param emac one past last macro in series
+ */
+LOCAL void
+gen_block(templ_t * tpl, macro_t * mac, macro_t * emac)
+{
+ /*
+ * Set up the processing context for this block of macros.
+ * It is used by the Guile callback routines and the exception
+ * handling code. It is all for user friendly diagnostics.
+ */
+ current_tpl = tpl;
+
+ while ((mac != NULL) && (mac < emac)) {
+ mac_func_t fc = mac->md_code;
+ if (fc >= FUNC_CT)
+ fc = FTYP_BOGUS;
+
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING)
+ trace_macro(tpl, mac);
+
+ cur_macro = mac;
+ mac = (*(load_procs[ fc ]))(tpl, mac);
+ ag_scribble_free();
+ }
+}
+
+/**
+ * Print out information about the invocation of a macro.
+ * Print up to the first 32 characters in the macro, for context.
+ *
+ * @param tpl template containing macros
+ * @param mac first macro in series
+ */
+static void
+trace_macro(templ_t * tpl, macro_t * mac)
+{
+ mac_func_t fc = mac->md_code;
+ if (fc >= FUNC_CT)
+ fc = FTYP_BOGUS;
+
+ fprintf(trace_fp, TRACE_MACRO_FMT, ag_fun_names[fc], mac->md_code,
+ tpl->td_file, mac->md_line);
+
+ if (mac->md_txt_off > 0) {
+ char * pz = tpl->td_text + mac->md_txt_off;
+ char * pe = BRK_NEWLINE_CHARS(pz);
+ if (pe > pz + 32)
+ pz = pz + 32;
+
+ putc(' ', trace_fp); putc(' ', trace_fp);
+ fwrite(pz, pe - pz, 1, trace_fp);
+ putc(NL, trace_fp);
+ }
+}
+
+/**
+ * The template output goes to stdout. Perhaps because output
+ * is for a CGI script. In any case, this case must be handled
+ * specially.
+ *
+ * @param tpl template to be processed
+ */
+static void
+do_stdout_tpl(templ_t * tpl)
+{
+ SCM res;
+
+ last_scm_cmd = NULL; /* We cannot be in Scheme processing */
+
+ switch (setjmp(abort_jmp_buf)) {
+ case SUCCESS:
+ break;
+
+ case PROBLEM:
+ if (*oops_pfx != NUL) {
+ fprintf(stdout, DO_STDOUT_TPL_ABANDONED, oops_pfx);
+ oops_pfx = zNil;
+ }
+ fclose(stdout);
+ return;
+
+ default:
+ fprintf(stdout, DO_STDOUT_TPL_BADR, oops_pfx);
+
+ case FAILURE:
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ curr_sfx = DO_STDOUT_TPL_NOSFX;
+ curr_def_ctx = root_def_ctx;
+ cur_fpstack = &out_root;
+ out_root.stk_fp = stdout;
+ out_root.stk_fname = DO_STDOUT_TPL_STDOUT;
+ out_root.stk_flags = FPF_NOUNLINK | FPF_STATIC_NM;
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING)
+ fputs(DO_STDOUT_TPL_START_STD, trace_fp);
+
+ /*
+ * IF there is a CGI prefix for error messages,
+ * THEN divert all output to a temporary file so that
+ * the output will be clean for any error messages we have to emit.
+ */
+ if (*oops_pfx == NUL)
+ gen_block(tpl, tpl->td_macros, tpl->td_macros + tpl->td_mac_ct);
+
+ else {
+ char const * pzRes;
+ (void)ag_scm_out_push_new(SCM_UNDEFINED);
+
+ gen_block(tpl, tpl->td_macros, tpl->td_macros + tpl->td_mac_ct);
+
+ /*
+ * Read back in the spooled output. Make sure it starts with
+ * a content-type: prefix. If not, we supply our own HTML prefix.
+ */
+ res = ag_scm_out_pop(SCM_BOOL_T);
+ pzRes = AG_SCM_CHARS(res);
+
+ /* 13 char prefix is: "content-type:" */
+ if (strneqvcmp(pzRes, DO_STDOUT_TPL_CONTENT, 13) != 0)
+ fputs(DO_STDOUT_TPL_CONTENT, stdout);
+
+ fwrite(pzRes, AG_SCM_STRLEN(res), 1, stdout);
+ }
+
+ fclose(stdout);
+}
+
+/**
+ * pop the current output spec structure. Deallocate it and the
+ * file name, too, if necessary.
+ */
+LOCAL out_spec_t *
+next_out_spec(out_spec_t * os)
+{
+ out_spec_t * res = os->os_next;
+
+ if (os->os_dealloc_fmt)
+ AGFREE(os->os_file_fmt);
+
+ AGFREE(os);
+ return res;
+}
+
+LOCAL void
+process_tpl(templ_t * tpl)
+{
+ /*
+ * IF the template file does not specify any output suffixes,
+ * THEN we will generate to standard out with the suffix set to zNoSfx.
+ * With output going to stdout, we don't try to remove output on errors.
+ */
+ if (output_specs == NULL) {
+ do_stdout_tpl(tpl);
+ return;
+ }
+
+ do {
+ out_spec_t * ospec = output_specs;
+
+ /*
+ * We cannot be in Scheme processing. We've either just started
+ * or we've made a long jump from our own code. If we've made a
+ * long jump, we've printed a message that is sufficient and we
+ * don't need to print any scheme expressions.
+ */
+ last_scm_cmd = NULL;
+
+ /*
+ * HOW was that we got here?
+ */
+ switch (setjmp(abort_jmp_buf)) {
+ case SUCCESS:
+ if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) {
+ fprintf(trace_fp, PROC_TPL_START, ospec->os_sfx);
+ fflush(trace_fp);
+ }
+ /*
+ * Set the output file name buffer.
+ * It may get switched inside open_output.
+ */
+ open_output(ospec);
+ memcpy(&out_root, cur_fpstack, sizeof(out_root));
+ AGFREE(cur_fpstack);
+ cur_fpstack = &out_root;
+ curr_sfx = ospec->os_sfx;
+ curr_def_ctx = root_def_ctx;
+ cur_fpstack->stk_flags &= ~FPF_FREE;
+ cur_fpstack->stk_prev = NULL;
+ gen_block(tpl, tpl->td_macros, tpl->td_macros+tpl->td_mac_ct);
+
+ do {
+ out_close(false); /* keep output */
+ } while (cur_fpstack->stk_prev != NULL);
+ break;
+
+ case PROBLEM:
+ /*
+ * We got here by a long jump. Close/purge the open files.
+ */
+ do {
+ out_close(true); /* discard output */
+ } while (cur_fpstack->stk_prev != NULL);
+ last_scm_cmd = NULL; /* "problem" means "drop current output". */
+ break;
+
+ default:
+ fprintf(trace_fp, PROC_TPL_BOGUS_RET, oops_pfx);
+ oops_pfx = zNil;
+ /* FALLTHROUGH */
+
+ case FAILURE:
+ /*
+ * We got here by a long jump. Close/purge the open files.
+ */
+ do {
+ out_close(true); /* discard output */
+ } while (cur_fpstack->stk_prev != NULL);
+
+ /*
+ * On failure (or unknown jump type), we quit the program, too.
+ */
+ processing_state = PROC_STATE_ABORTING;
+ do ospec = next_out_spec(ospec);
+ while (ospec != NULL);
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ output_specs = next_out_spec(ospec);
+ } while (output_specs != NULL);
+}
+
+
+LOCAL void
+out_close(bool purge)
+{
+ if ((cur_fpstack->stk_flags & FPF_NOCHMOD) == 0)
+ make_readonly();
+
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, OUT_CLOSE_TRACE_WRAP, __func__,
+ cur_fpstack->stk_fname);
+
+ fclose(cur_fpstack->stk_fp);
+
+ /*
+ * Only stdout and /dev/null are marked, "NOUNLINK"
+ */
+ if ((cur_fpstack->stk_flags & FPF_NOUNLINK) == 0) {
+ /*
+ * IF we are told to purge the file OR the file is an AutoGen temp
+ * file, then get rid of the output.
+ */
+ if (purge || ((cur_fpstack->stk_flags & FPF_UNLINK) != 0))
+ unlink(cur_fpstack->stk_fname);
+
+ else {
+ struct utimbuf tbuf;
+
+ tbuf.actime = time(NULL);
+ tbuf.modtime = outfile_time;
+
+ /*
+ * The putative start time is one second earlier than the
+ * earliest output file time, regardless of when that is.
+ */
+ if (outfile_time <= start_time)
+ start_time = outfile_time - 1;
+
+ utime(cur_fpstack->stk_fname, &tbuf);
+ }
+ }
+
+ /*
+ * Do not deallocate statically allocated names
+ */
+ if ((cur_fpstack->stk_flags & FPF_STATIC_NM) == 0)
+ AGFREE((void*)cur_fpstack->stk_fname);
+
+ /*
+ * Do not deallocate the root entry. It is not allocated!!
+ */
+ if ((cur_fpstack->stk_flags & FPF_FREE) != 0) {
+ out_stack_t* p = cur_fpstack;
+ cur_fpstack = p->stk_prev;
+ AGFREE((void*)p);
+ }
+}
+
+/**
+ * Figure out what to use as the base name of the output file.
+ * If an argument is not provided, we use the base name of
+ * the definitions file.
+ */
+static void
+open_output(out_spec_t * spec)
+{
+ static char const write_mode[] = "w" FOPEN_BINARY_FLAG "+";
+
+ char const * out_file = NULL;
+
+ if (strcmp(spec->os_sfx, OPEN_OUTPUT_NULL) == 0) {
+ static int const flags = FPF_NOUNLINK | FPF_NOCHMOD | FPF_TEMPFILE;
+ null_open:
+ open_output_file(DEV_NULL, DEV_NULL_LEN, write_mode, flags);
+ return;
+ }
+
+ /*
+ * IF we are to skip the current suffix,
+ * we will redirect the output to /dev/null and
+ * perform all the work. There may be side effects.
+ */
+ if (HAVE_OPT(SKIP_SUFFIX)) {
+ int ct = STACKCT_OPT(SKIP_SUFFIX);
+ const char ** ppz = STACKLST_OPT(SKIP_SUFFIX);
+
+ while (--ct >= 0) {
+ if (strcmp(spec->os_sfx, *ppz++) == 0)
+ goto null_open;
+ }
+ }
+
+ /*
+ * Remove any suffixes in the last file name
+ */
+ {
+ char const * def_file = OPT_ARG(BASE_NAME);
+ char z[AG_PATH_MAX];
+ const char * pst = strrchr(def_file, '/');
+ char * end;
+
+ pst = (pst == NULL) ? def_file : (pst + 1);
+
+ /*
+ * We allow users to specify a suffix with '-' and '_', but when
+ * stripping a suffix from the "base name", we do not recognize 'em.
+ */
+ end = strchr(pst, '.');
+ if (end != NULL) {
+ size_t len = (unsigned)(end - pst);
+ if (len >= sizeof(z))
+ AG_ABEND("--base-name name is too long");
+
+ memcpy(z, pst, len);
+ z[ end - pst ] = NUL;
+ pst = z;
+ }
+
+ /*
+ * Now formulate the output file name in the buffer
+ * provided as the input argument.
+ */
+ out_file = aprf(spec->os_file_fmt, pst, spec->os_sfx);
+ if (out_file == NULL)
+ AG_ABEND(aprf(OPEN_OUTPUT_BAD_FMT, spec->os_file_fmt, pst,
+ spec->os_sfx));
+ }
+
+ open_output_file(out_file, strlen(out_file), write_mode, 0);
+ free((void *)out_file);
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/tpProcess.c */