diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2012-08-11 16:45:31 +0000 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-10-18 12:43:06 +0000 |
commit | 7c432b265ed7ca5f8304938db73912df8ce35032 (patch) | |
tree | c95de7bee7b742ed92b9924d904adca706e48d80 /agen5 | |
download | autogen-master.tar.gz |
Imported from /srv/lorry/lorry-area/autogen/autogen-5.16.2.tar.gz.HEADautogen-5.16.2master
Diffstat (limited to 'agen5')
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 = 'ma';\n" + "var two = 'ilt';\n" + "document.write('<a href=\"' + one + two );\n" + "document.write('o:\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" + "'(\"&\" \"<\" \">\"))))\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 "&" "<" ">")) + * @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 = ⅈ + + 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>...<...>...</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{"&"}, @code{"<"}, and +;;; * @code{">"}, respectively). +;;; =*/ +;;; +(define html-escape-encode (lambda (str) + (string-substitute str + '("&" "<" ">") + '("&" "<" ">") ) )) + +(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 = 'ma'; +var two = 'ilt'; +document.write('<a href="' + one + two ); +document.write('o:bkorb@gnu.org'); +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 */ |